OSDN Git Service

Modified: Upload, Command execution
[eos/zephyr.git] / server / class / DB.js
1 /**
2  * DBを操作するクラス
3  *
4  * @returns {object}
5  */
6 'use strict';
7
8 var fs = require('fs');
9 var Sequelize = require('sequelize');
10 var co = require('co');
11 var uuid = require('node-uuid');
12
13 var sequelize;
14 var test = 'hello';
15 var Files; // Model object of sequelize
16
17 var instance = {
18     init: init,
19     sayHello: function() {
20         return test;
21     },
22     getDirectoryParentId: getDirectoryParentId,
23     getDirectoryId: getDirectoryId,
24     getDirectory: getDirectory,
25     getFiles: getFiles,
26     getDirectories: getDirectories,
27     existFile: existFile,
28     existFileId: existFileId,
29     notExistFile: notExistFile,
30     existDirectory: existDirectory,
31     notExistDirectory: notExistDirectory,
32     createFile: createFile,
33     removeFile: removeFile,
34     createDirectory: createDirectory,
35     //test1: test1,
36     //test2: test2,
37     testRest: testRest
38     /*
39     removeDirectory: removeDirectory,
40     getFilename: getFilename,
41     moveFile: moveFile,
42     moveDirectory: moveDirectory
43     */
44 };
45
46 /**
47  * init
48  * @constructor
49  * @returns {promise}
50  */
51
52 /**
53  * productionモード時は、db.workspaceを永続化させる
54  * DEBUGモードの時は、db.debugを実行毎に作成する。
55  * DEBUGモードの時は、sync()の中で、/レコードがFilesテーブルに追加される。
56  * モードの切り替えは環境変数NODE_ENVで行っている。
57  * zephyr serveコマンドが実行されるとNODE_ENV = 'production'
58  * zephyr debugコマンドが実行されるとNODE_ENV = 'debug'
59  * となる。
60  * それぞれの設定は$ZEOHYR_HOME/cli/zephyr-serve, $ZEPHYR_HOME/zephyre_debugを参照
61  */
62
63     function init() {
64         var dbPath;
65         var dbOption = {
66             dialect: 'sqlite'
67         };
68
69         if(process.env['NODE_ENV'] === 'production') {
70             dbPath = __dirname + '/../../user-specific-files/db/db.workspace';
71             process.env['WorkspacePATH']= __dirname + '/../../user-specific-files/workspace';
72
73             // if doesn't exist workspace.db, create.
74             try {
75                 fs.accessSync(dbPath, fs.R_OK | fs.W_OK);
76             } catch(e) {
77                 fs.writeFileSync(dbPath, '');
78             }
79             dbOption.storage = dbPath;
80
81         } else if(process.env['NODE_ENV'] === 'debug') {
82             dbPath = __dirname + '/../../user-specific-files/db/db.debug';
83             process.env['WorkspacePATH']= __dirname + '/../../user-specific-files/workspace.debug';
84
85             try {
86                 fs.accessSync(dbPath, fs.R_OK | fs.W_OK);
87                 fs.unlinkSync(dbPath);
88             } catch(e) {
89             }
90
91             fs.writeFileSync(dbPath, '');
92             dbOption.storage = dbPath;
93         }
94         var workspacePath = process.env['WorkspacePATH'];
95
96         sequelize = new Sequelize('','','', dbOption);
97
98         Files = sequelize.define('file', {
99             fileId: {
100                 type: Sequelize.UUID,
101                 field: 'file_id',
102                 primaryKey: true
103             },
104             name: {
105                 type: Sequelize.STRING,
106                 field: 'name',
107                 allowNull: false
108             },
109             parentId: {
110                 type: Sequelize.UUID,
111                 field: 'parent_id',
112                 allowNull: false
113             },
114             fileType: {
115                 type: Sequelize.ENUM(0,1),
116                 field: 'file_type',
117                 allowNull: false
118             }
119         });
120
121         var force = process.env.NODE_ENV !== 'production';
122         return Files.sync({force: force})
123         .then(function() {
124             return insertRoot();
125         });
126
127
128     }
129
130
131
132 /**
133  * sync
134  *
135  * @returns {promise}<DBオブジェクトの関数群のオブジェクトをresolveする>
136  */
137 function insertRoot() {
138     return new Promise(function(resolve, reject) {
139         var q = {
140             where: {
141                 name: '/'
142             }
143         };
144         Files.findOne(q)
145         .then(function(r) {
146             if(r === null) {
147                 var root = {
148                     fileId: '1f83f620-c1ed-11e5-9657-7942989daa00', // rootのuuidは固定値
149                     name: '/',
150                     parentId: '',
151                     fileType: 0
152                 };
153                 return Files.create(root);
154             }
155         })
156         .then(function() {
157             resolve();
158         });
159     });
160 }
161
162 /**
163  * getDirectoryParentId
164  * ディレクトリのparentIdを取得する
165  * @param name
166  * @returns {promise} ディレクトリが存在すればresolve(uuid), しなければreject
167  */
168 function getDirectoryParentId(name) {
169     return new Promise(function(resolve, reject) {
170         var q = {
171             where: {
172                 name: name,
173                 fileType: 0
174             }
175         };
176         Files.findAll(q)
177         .then(function(r) {
178             if(r.length === 0) {
179                 reject(new Error('"' + name + '" directory doesn\'t exist.'));
180             } else {
181                 var map = r.map(function(c) { return c.dataValues.parentId });
182                 resolve(map);
183             }
184         });
185     });
186 }
187
188
189 /**
190  * getDirectoryId
191  * ディレクトリのfileIdを取得する
192  * @param name
193  * @returns {promise} ディレクトリが存在すればresolve(uuid), しなければreject
194  */
195
196 function getDirectoryId(name) {
197     return new Promise(function(resolve, reject) {
198         var q = {
199             where: {
200                 name: name,
201                 fileType: 0
202             }
203         };
204         Files.findAll(q)
205         .then(function(r) {
206             if(r.length === 0) {
207                 reject(new Error('"' + name + '" directory doesn\'t exist.'));
208             } else {
209                 var map = r.map(function(c) { return c.dataValues.fileId });
210                 resolve(map);
211             }
212         });
213     });
214 }
215
216 /**
217  * getDirectory
218  * ディレクトリのfileIdを取得する
219  * @param name
220  * @returns {promise} ディレクトリが存在すればresolve(name), しなければreject
221  */
222
223 function getDirectory(name) {
224     return new Promise(function(resolve, reject) {
225         var q = {
226             where: {
227                 name: name,
228                 fileType: 0
229             }
230         };
231         Files.findAll(q)
232         .then(function(r) {
233             if(r.length === 0) {
234                 reject(new Error('"' + name + '" directory doesn\'t exist.'));
235             } else {
236                 var map = r.map(function(c) { return c.dataValues });
237                 resolve(map);
238             }
239         });
240     });
241 }
242
243 /**
244  * getFiles
245  * ディレクトリのfileId一覧を取得する
246  * @param {string} fileId ディレクトリのfileId
247  * @returns {promise} resolve([Array]<string>fileId) 引数で与えられたディレクトリを直接の親に持つファイルのレコードの配列を返す。
248  *                    与えられたfileIdがディレクトリでなければresolve
249  */
250 function getFiles(fileId) {
251     return new Promise(function(resolve, reject) {
252         if(!fileId) {
253             reject(new Error('"'+fileId+'" is invalid.'));
254         }
255         var q = {
256             where: {
257                 parentId: fileId
258             }
259         };
260         Files.findAll(q)
261         .then(function(r) {
262             if(r.length === 0) {
263                 reject(new Error('"'+fileId+'" is invalid.'));
264             }
265             var files = r.map(function(data) { return data; });
266             resolve(files);
267         });
268     });
269 }
270
271 /**
272  * getDirectories
273  * ディレクトリの一覧を取得する
274  * @returns {promise} resolve([Array]<Object>)
275  */
276 function getDirectories() {
277     return new Promise(function(resolve, reject) {
278         var q = {
279             where: {
280                 fileType: 0
281             }
282         };
283         Files.findAll(q)
284         .then(function(r) {
285             var dirs = r.map(function(data) {
286                 return data.dataValues;
287             });
288             resolve(dirs);
289         });
290     });
291 }
292
293 /**
294  * existFile
295  * 同一ディレクトリに同名のファイルが存在することを確かめる
296  * @param {string} fileName
297  * @param {string} parentDirectory parentDirectoryの絶対パス
298  * @returns {promise} ファイルが存在すればresolve、存在しなければreject
299  */
300 function existFile(fileName, parentDirectory) {
301     return new Promise(function(resolve, reject) {
302         existDirectory(parentDirectory)
303         .catch(function(error) {
304             reject(error);
305         })
306         .then(function(fileId) {
307             var q = {
308                 where: {
309                     name: fileName,
310                     parentId: fileId
311                 }
312             };
313             return  Files.findOne(q)
314         })
315         .then(function(r) {
316             if(r === null) {
317                 reject(new Error("\"" + fileName + "\" does not exist in " + '"' + parentDirectory + "\" directory."));
318             } else {
319                 resolve(r.fileId);
320             }
321         });
322     });
323 }
324
325 /**
326  * existFileId
327  * 同一ディレクトリに同名のファイルが存在することを確かめる
328  * @param {string} fileId
329  * @param {string} parentDirectory parentDirectoryの絶対パス
330  * @returns {promise} ファイルが存在すればresolve、存在しなければreject
331  */
332 function existFileId(whichfileID, parentDirectory) {
333     console.log('findFileId start');
334
335     return new Promise(function(resolve, reject) {
336         existDirectory(parentDirectory)
337         .catch(function(error) {
338             reject(error);
339         })
340         .then(function(fileId) {
341             console.log('findFileId: '+whichfileID);
342             var q =  Files.findOne({
343                 where: {
344                     fileId: whichfileID,
345                     parentId: fileId
346                 }
347             });
348             console.log('findFileId: get q'+q.tostring);
349             if(q) {
350               return true;
351             } else {
352               return false;
353             }
354         })
355         .then(function(r) {
356             if(r === null) {
357                 reject(new Error("\"" + fileName + "\" does not exist in " + '"' + parentDirectory + "\" directory."));
358             } else {
359                 resolve(r.fileId);
360             }
361         });
362     });
363 }
364
365
366 /**
367  * notExistFile
368  * 同一ディレクトリに同名のファイルが存在していないことを確かめる
369  * @param {string}fileName
370  * @param {string}parentDirectory
371  * @returns {promise} ファイルが存在しなければresolve、存在すればreject
372  */
373 function notExistFile(fileName, parentDirectory) {
374     return new Promise(function(resolve, reject) {
375         existDirectory(parentDirectory)
376         .catch(function(error) {
377             reject(error);
378         })
379         .then(function(fileId) {
380             var q = {
381                 where: {
382                     name: fileName,
383                     parentId: fileId
384                 }
385             };
386             return Files.findOne(q)
387         })
388         .then(function(r) {
389             if(r === null) {
390                 resolve();
391             } else {
392                 reject(new Error("\"" + fileName + "\" has already existed in " + '"' + parentDirectory + "\" directory."));
393             }
394         });
395     });
396 }
397
398 /**
399  * existDirectory
400  * ディレクトリが存在することを確認する
401  * @param {string} directory
402  * @returns {promise} ディレクトリが存在すればresolve{fileId)、存在しなければreject
403  */
404 function existDirectory(directory) {
405     return new Promise(function(resolve, reject) {
406         if(!directory) {
407             reject(new Error('parameter "directory" is undefined'));
408         }
409
410         var arrayDirectory;
411         var root = directory.substr(0,1);
412
413         if(root !== '/') {
414             reject(new Error('directory name should start "/" so that it is absolute path including root.'));
415         }
416
417         if(directory === '/') {
418             resolve('1f83f620-c1ed-11e5-9657-7942989daa00'); // rootのuuid
419         } else {
420             arrayDirectory = directory.split('/');
421             arrayDirectory.shift(); // root
422             arrayDirectory.unshift('/');
423         }
424
425         var directoriesPromise = arrayDirectory.map(function(name) {
426             return getDirectory(name);
427         });
428
429         Promise.all(directoriesPromise)
430         .then(function(r) {
431             var parentId = r[0][0].fileId;
432             var index;
433             for(var i=1;i<r.length;i++) {
434                 index = r[i].map(function(c) { return c.parentId }).indexOf(parentId);
435                 if(index > -1) {
436                     parentId = r[i][index].fileId;
437                 } else {
438                     reject(new Error('"' + directory + '" directory doesn\'t exist.'));
439                 }
440             }
441             resolve(parentId);
442         })
443         .catch(function(error) {
444             reject(new Error('"' + directory + '" directory doesn\'t exist.'));
445         });
446     });
447 }
448
449 /**
450  * notExistDirectory
451  * ディレクトリが存在しないことを確認する
452  * @param {string} directory
453  * @returns {promise} ディレクトリが存在しなければresolve、存在すればreject
454  */
455 function notExistDirectory(directory) {
456     return new Promise(function(resolve, reject) {
457         if(!directory) {
458             resolve();
459         }
460
461         var arrayDirectory;
462         var root = directory.substr(0,1);
463
464         if(root !== '/') {
465             resolve();
466         }
467
468         if(directory === '/') {
469             reject(new Error('"' + directory + '" directory exists.'));
470         } else {
471             arrayDirectory = directory.split('/');
472             arrayDirectory.shift(); // root
473             arrayDirectory.unshift('/');
474         }
475
476         var directoriesPromise = arrayDirectory.map(function(name) {
477             return getDirectory(name);
478         });
479         Promise.all(directoriesPromise)
480         .then(function(r) {
481             var parentId = r[0][0].fileId;
482             var index;
483             for(var i=1;i<r.length;i++) {
484                 index = r[i].map(function(c) { return c.parentId }).indexOf(parentId);
485                 if(index > -1) {
486                     parentId = r[i][index].fileId;
487                 } else {
488                     resolve();
489                 }
490             }
491             reject(new Error('"' + directory + '" directory exists.'));
492         })
493         .catch(function(error) {
494             resolve();
495         });
496     });
497 }
498
499 /**
500  * createFile
501  *
502  * @param fileName
503  * @param parentDirectory
504  * @returns {promise}<sequelize.createの結果を格納したobject | Error>
505  */
506 function createFile(fileName,parentDirectory) {
507     return new Promise(function(resolve, reject) {
508         if(!fileName) {
509             reject(new Error('filename is required.'));
510         }
511         Promise.all([existDirectory(parentDirectory), notExistFile(fileName, parentDirectory) ])
512         .catch(function(error) {
513             reject(error);
514         })
515         .then(function(r) {
516             var parentId = r[0]
517             var q = {
518                 fileId: uuid.v1(),
519                 name: fileName,
520                 parentId: parentId,
521                 fileType: 1
522             }
523
524             console.log('createFile:'+process.env['WorkspacePATH']+parentDirectory+fileName);
525             console.log('createFile:'+process.env['WorkspacePATH']+parentDirectory+q.fileId);
526             //console.log('createFile:'+workspacePath+parentDirectory+fileName);
527             //console.log('createFile:'+workspacePath+parentDirectory+r.dataValues.fileId);
528
529             fs.rename(process.env['WorkspacePATH']+parentDirectory+fileName, process.env['WorkspacePATH']+parentDirectory+q.fileId, function (err) {
530               if (err) throw err;
531               console.log('renamed complete');
532             });
533
534             return Files.create(q)
535         })
536         .then(function(r) {
537             resolve(r.dataValues.fileId);
538         });
539     });
540 }
541
542
543 /**
544  * removeFile
545  * ファイルを削除する
546  * @param {string} fileName
547  * @param {string} parentDirectory
548  * @returns {promise} ファイル削除に成功すればresolve、失敗すればreject
549  */
550 function removeFile(fileName, parentDirectory) {
551     return new Promise(function(resolve, reject) {
552         existFile(fileName, parentDirectory)
553         .catch(function(error) {
554             reject(error);
555         })
556         .then(function(fileId) {
557             var q = {
558                 where: {
559                     fileId: fileId
560                 }
561             };
562             return Files.destroy(q);
563         })
564         .then(function() {
565             resolve();
566         });
567     });
568 }
569
570 /**
571  * createDirectory
572  * ディレクトリを作成
573  * @param directory
574  * @returns {promise} ディレクトリの作成に成功すればresolve、失敗すればreject
575  */
576 function createDirectory(directory) {
577     return new Promise(function(resolve, reject) {
578         if(!directory) {
579             reject(new Error('directory name should start "/" so that it is absolute path including root.'));
580         }
581
582         var leaf = directory.split('/').pop();
583         var parentDirectory = directory.replace('/'+leaf, '');
584         if(!parentDirectory) {
585             parentDirectory = '/';
586         }
587         Promise.all([existDirectory(parentDirectory), notExistDirectory(directory), notExistFile(leaf, parentDirectory)])
588         .catch(function(error) {
589             reject(error);
590         })
591         .then(function(r) {
592             var parentId = r[0];
593             var q = {
594                 fileId: uuid.v1(),
595                 name: leaf,
596                 parentId: parentId,
597                 fileType: 0
598             }
599             return Files.create(q)
600         })
601         .then(function(r) {
602             resolve(r.dataValues.fileId);
603         });
604     });
605 }
606
607
608 /**
609  * removeDirectory
610  * ディレクトリを削除
611  * @param directory
612  * @returns {promise} ディレクトリの削除に成功すればresolve、失敗すればreject
613  */
614 function removeDirectory(directory) {
615     return new Promise(function(resolve, reject) {
616         var leaf = directory.split('/').pop();
617         var parentDirectory = directory.replace('/'+leaf, '');
618         if(!parentDirectory) {
619             parentDirectory = '/';
620         }
621         Promise.all([existDirectory(parentDirectory), notExistDirectory(directory), notExistFile(leaf, parentDirectory)])
622         .catch(function(error) {
623             reject(error);
624         })
625         .then(function(r) {
626             var parentId = r[0];
627             var q = {
628                 fileId: uuid.v1(),
629                 name: leaf,
630                 parentId: parentId,
631                 fileType: 0
632             }
633             return Files.create(q)
634         })
635         .then(function(r) {
636             resolve(r.dataValues.fileId);
637         });
638     });
639 }
640
641 /**
642  * test1
643  * test用にデータベースのレコードを追加する関数
644  * @returns {promise}
645  */
646
647 function test1() {
648     var q = {
649         fileId: uuid.v1(),
650         name: 'hoge.txt',
651         parentId: '1f83f620-c1ed-11e5-9657-7942989daa00', // rootのuuid
652         fileType: 1
653     };
654     return Files.create(q);
655 }
656
657 /**
658  * test2
659  * test用にデータベースのレコードを追加する関数
660  * @returns {promise}
661  */
662
663 function test2() {
664     var q1 = {
665         fileId: '1f83f620-c1ed-11e5-9657-7942989daa04',
666         name: 'one',
667         parentId: '1f83f620-c1ed-11e5-9657-7942989daa00', // rootのuuid
668         fileType: 0 //directory
669     };
670     return Files.create(q1)
671     .then(function() {
672         var q2 = {
673             fileId: '1f83f620-c1ed-11e5-9657-7942989daa01',
674             name: 'two',
675             parentId: q1.fileId,
676             fileType: 0
677
678         };
679         return Files.create(q2);
680     })
681     .then(function(r) {
682         var q3 = {
683             fileId: '1f83f620-c1ed-11e5-9657-7942989daa02',
684             name: 'two',
685             parentId: '1f83f620-c1ed-11e5-9657-7942989daa00', //rootのuuid
686             fileType: 0
687         };
688         return Files.create(q3);
689     })
690     .then(function() {
691         var q4 = {
692             fileId:'1f83f620-c1ed-11e5-9657-7942989daa03',
693             name: 'hogehoge.txt',
694             parentId: q1.fileId,
695             fileType: 1
696         };
697         return Files.create(q4);
698     });
699 }
700
701
702 function testRest() {
703     var q = {
704         fileId: '54998540-f62c-11e6-abc9-45e2912c9ebf',
705         name: 'debugRest.mrc',
706         parentId: '1f83f620-c1ed-11e5-9657-7942989daa00', //rootのuuid
707         fileType: 1 // file
708     };
709     return Files.create(q);
710 }
711
712 module.exports = { instance: instance };