OSDN Git Service

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