mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-10-31 18:16:00 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			357 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| 
 | |
| import { http_get, http_post, prepare, basename, dirname } from '../helpers/';
 | |
| import Path from 'path';
 | |
| 
 | |
| import { Observable } from 'rxjs/Observable';
 | |
| import { cache } from '../helpers/';
 | |
| 
 | |
| class FileSystem{
 | |
|     constructor(){
 | |
|         this.obs = null;
 | |
|         this.current_path = null;
 | |
|     }
 | |
| 
 | |
|     ls(path, internal = false){
 | |
|         this.current_path = path;
 | |
|         this.obs && this.obs.complete();
 | |
| 
 | |
|         return Observable.create((obs) => {
 | |
|             this.obs = obs;
 | |
|             let keep_pulling_from_http = false;
 | |
|             this._ls_from_cache(path, true)
 | |
|                 .then(() => {
 | |
|                     const fetch_from_http = (_path) => {
 | |
|                         return this._ls_from_http(_path)
 | |
|                             .then(() => new Promise((done, err) => {
 | |
|                                 window.setTimeout(() => done(), 2000);
 | |
|                             }))
 | |
|                             .then(() => {
 | |
|                                 return keep_pulling_from_http === true? fetch_from_http(_path) : Promise.resolve();
 | |
|                             });
 | |
|                     };
 | |
|                     fetch_from_http(path);
 | |
|                 });
 | |
| 
 | |
|             return () => {
 | |
|                 keep_pulling_from_http = false;
 | |
|             };
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     _ls_from_http(path){
 | |
|         const url = '/api/files/ls?path='+prepare(path);
 | |
|         return http_get(url).then((response) => {
 | |
|             return cache.get(cache.FILE_PATH, path, false).then((_files) => {
 | |
|                 if(_files && _files.results){
 | |
|                     let _files_virtual_to_keep = _files.results.filter((file) => {
 | |
|                         return file.icon === 'loading' &&
 | |
|                             (new Date()).getTime() - file._timestamp < 1000*60*60;
 | |
|                     });
 | |
|                     // update file results
 | |
|                     for(let i=0; i<_files_virtual_to_keep.length; i++){
 | |
|                         for(let j=0; j<response.results.length; j++){
 | |
|                             if(response.results[j].name === _files_virtual_to_keep[i].name){
 | |
|                                 response.results[j] = Object.assign({}, _files_virtual_to_keep[i]);
 | |
|                                 _files_virtual_to_keep[i] = null;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     // add stuff that didn't exist in our response
 | |
|                     _files_virtual_to_keep = _files_virtual_to_keep.filter((e) => e);
 | |
|                     response.results = response.results.concat(_files_virtual_to_keep);
 | |
|                 }
 | |
|                 // publish
 | |
|                 cache.put(cache.FILE_PATH, path, {results: response.results});
 | |
|                 if(this.current_path === path) this.obs && this.obs.next(response.results);
 | |
|             });
 | |
|         }).catch((_err) => {
 | |
|             // TODO: user is in offline mode, notify
 | |
|             console.log(_err);
 | |
|         });
 | |
|     }
 | |
|     _ls_from_cache(path, _record_access = false){
 | |
|         return cache.get(cache.FILE_PATH, path, _record_access).then((_files) => {
 | |
|             if(_files && _files.results){
 | |
|                 if(this.current_path === path){
 | |
|                     this.obs && this.obs.next(_files.results);
 | |
|                 }
 | |
|             };
 | |
|             return Promise.resolve();
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     rm(path){
 | |
|         const url = '/api/files/rm?path='+prepare(path);
 | |
|         this._replace(path, 'loading')
 | |
|             .then(() => http_get(url))
 | |
|             .then((res) => {
 | |
|                 if(res.status === 'ok'){
 | |
|                     cache.remove(cache.FILE_CONTENT, path, false);
 | |
|                     cache.remove(cache.FILE_PATH, dirname(path), false);
 | |
|                     return this._remove(path);
 | |
|                 }else{
 | |
|                     return this._replace(path, 'error');
 | |
|                 }
 | |
|             });
 | |
|     }
 | |
| 
 | |
|     cat(path){
 | |
|         const url = '/api/files/cat?path='+prepare(path);
 | |
|         return http_get(url, 'raw')
 | |
|             .then((res) => cache.put(cache.FILE_CONTENT, path, {result: res}))
 | |
|             .catch((res) => {
 | |
|                 return cache.get(cache.FILE_CONTENT, path)
 | |
|                     .then((_res) => {
 | |
|                         if(!_res || !_res.result) return Promise.reject(_res);
 | |
|                         return Promise.resolve(_res.result);
 | |
|                     })
 | |
|                     .catch(() => Promise.reject(res));
 | |
|             });
 | |
|     }
 | |
|     url(path){
 | |
|         const url = '/api/files/cat?path='+prepare(path);
 | |
|         return Promise.resolve(url);
 | |
|     }
 | |
| 
 | |
|     save(path, file){
 | |
|         const url = '/api/files/cat?path='+prepare(path);
 | |
|         let formData = new window.FormData();
 | |
|         formData.append('file', file);
 | |
|         return this._replace(path, 'loading')
 | |
|             .then(() => cache.put(cache.FILE_CONTENT, path, file))
 | |
|             .then(() => http_post(url, formData, 'multipart'))
 | |
|             .then((res)=> res.status === 'ok'? this._replace(path) : this._replace(path, 'error'));
 | |
|     }
 | |
| 
 | |
|     mkdir(path){
 | |
|         const url = '/api/files/mkdir?path='+prepare(path);
 | |
|         this._add(path, 'loading')
 | |
|             .then(() => this._add(path, 'loading'))
 | |
|             .then(() => http_get(url))
 | |
|             .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error'));
 | |
|     }
 | |
| 
 | |
|     touch(path, file){
 | |
|         this._add(path, 'loading')
 | |
|             .then(() => {
 | |
|                 if(file){
 | |
|                     const url = '/api/files/cat?path='+prepare(path);
 | |
|                     let formData = new window.FormData();
 | |
|                     formData.append('file', file);
 | |
|                     return http_post(url, formData, 'multipart');
 | |
|                 }else{
 | |
|                     const url = '/api/files/touch?path='+prepare(path);
 | |
|                     return http_get(url);
 | |
|                 }
 | |
|             })
 | |
|             .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error'));
 | |
|     }
 | |
| 
 | |
|     mv(from, to){
 | |
|         const url = '/api/files/mv?from='+prepare(from)+"&to="+prepare(to);
 | |
| 
 | |
|         ui_before_request(from, to)
 | |
|             .then(() => this._ls_from_cache(dirname(from)))
 | |
|             .then(() => this._ls_from_cache(dirname(to)))
 | |
|             .then(() => http_get(url)
 | |
|                   .then((res) => {
 | |
|                       if(res.status === 'ok'){
 | |
|                           return ui_when_success.call(this, from, to)
 | |
|                               .then(() => this._ls_from_cache(dirname(from)))
 | |
|                               .then(() => this._ls_from_cache(dirname(to)));
 | |
|                       }else{
 | |
|                           return ui_when_fail.call(this, from, to)
 | |
|                               .then(() => this._ls_from_cache(dirname(from)))
 | |
|                               .then(() => this._ls_from_cache(dirname(to)));
 | |
|                       }
 | |
|                   }));
 | |
| 
 | |
|         function ui_before_request(from, to){
 | |
|             return update_from()
 | |
|                 .then((file) => {
 | |
|                     if(dirname(from) !== dirname(to)){
 | |
|                         return update_to(file);
 | |
|                     }
 | |
|                     return Promise.resolve();
 | |
|                 });
 | |
| 
 | |
|             function update_from(){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(from), false)
 | |
|                     .then((res_from) => {
 | |
|                         let _file = {name: basename(from), type: /\/$/.test(from) ? 'directory' : 'file'};
 | |
|                         res_from.results = res_from.results.map((file) => {
 | |
|                             if(file.name === basename(from)){
 | |
|                                 file.name = basename(to);
 | |
|                                 file.icon = 'loading';
 | |
|                                 file._timestamp = (new Date()).getTime();
 | |
|                                 _file = file;
 | |
|                             }
 | |
|                             return file;
 | |
|                         });
 | |
|                         return cache.put(cache.FILE_PATH, dirname(from), res_from)
 | |
|                             .then(() => Promise.resolve(_file));
 | |
|                     });
 | |
|             }
 | |
|             function update_to(file){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(to), false).then((res_to) => {
 | |
|                     if(!res_to || !res_to.results) return Promise.resolve();
 | |
|                     res_to.results.push(file);
 | |
|                     return cache.put(cache.FILE_PATH, dirname(to), res_to);
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         function ui_when_fail(from, to){
 | |
|             return update_from()
 | |
|                 .then((file) => {
 | |
|                     if(dirname(from) !== dirname(to)){
 | |
|                         return update_to();
 | |
|                     }
 | |
|                     return Promise.resolve();
 | |
|                 });
 | |
| 
 | |
|             function update_from(){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(from), false)
 | |
|                     .then((res_from) => {
 | |
|                         if(!res_from || !res_from.results) return Promise.reject();
 | |
|                         res_from.results = res_from.results.map((file) => {
 | |
|                             if(file.name === basename(from)){
 | |
|                                 file.icon = 'error';
 | |
|                             }
 | |
|                             return file;
 | |
|                         });
 | |
|                         return cache.put(cache.FILE_PATH, dirname(from), res_from)
 | |
|                             .then(() => Promise.resolve());
 | |
|                     });
 | |
|             }
 | |
| 
 | |
|             function update_to(){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(to), false)
 | |
|                     .then((res_to) => {
 | |
|                         if(!res_to || !res_to.results) return Promise.resolve();
 | |
|                         res_to.results = res_to.results.filter((file) => {
 | |
|                             if(file.name === basename(to)){
 | |
|                                 return false;
 | |
|                             }
 | |
|                             return true;
 | |
|                         });
 | |
|                         return cache.put(cache.FILE_PATH, dirname(from), res_to);
 | |
|                     });
 | |
|             }
 | |
|         }
 | |
|         function ui_when_success(from, to){
 | |
|             if(dirname(from) === dirname(to)){
 | |
|                 return this._replace(dirname(from)+basename(to), null);
 | |
|             }else{
 | |
|                 return update_from()
 | |
|                     .then(update_to)
 | |
|                     .then(update_related);
 | |
|             }
 | |
| 
 | |
|             function update_from(){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(from), false).then((res_from) => {
 | |
|                     if(!res_from || !res_from.results) return Promise.resolve();
 | |
|                     res_from.results = res_from.results.filter((file) => {
 | |
|                         if(file.name === basename(to)){
 | |
|                             return false;
 | |
|                         }
 | |
|                         return true;
 | |
|                     });
 | |
|                     return cache.put(cache.FILE_PATH, dirname(from), res_from);
 | |
|                 });
 | |
|             }
 | |
|             function update_to(){
 | |
|                 return cache.get(cache.FILE_PATH, dirname(to), false).then((res_to) => {
 | |
|                     const target_already_exist = res_to && res_to.results ? true : false;
 | |
|                     if(target_already_exist){
 | |
|                         res_to.results = res_to.results.map((file) => {
 | |
|                             if(file.name === basename(to)){
 | |
|                                 delete file.icon;
 | |
|                             }
 | |
|                             return file;
 | |
|                         });
 | |
|                         return cache.put(cache.FILE_PATH, dirname(to), res_to);
 | |
|                     }else{
 | |
|                         const data = {results: [{
 | |
|                             name: basename(to),
 | |
|                             type: /\/$/.test(to) ? 'directory' : 'file',
 | |
|                             time: (new Date()).getTime()
 | |
|                         }]};
 | |
|                         return cache.put(cache.FILE_PATH, dirname(to), data);
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|             function update_related(){
 | |
|                 // manage nested directories when we try to rename a directory
 | |
|                 if(/\/$/.test(from) === true){
 | |
|                     return cache.update_path((data) => {
 | |
|                         if(data.path !== dirname(to) && data.path !== dirname(from) && data.path.indexOf(dirname(from)) === 0){
 | |
|                             const old_path = data.path;
 | |
|                             data.path = data.path.replace(dirname(from), dirname(to));
 | |
|                             return cache.remove(cache.FILE_PATH, old_path)
 | |
|                                 .then(() => cache.put(cache.FILE_PATH, data.path, data));
 | |
|                         }
 | |
|                         return Promise.resolve();
 | |
|                     });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     _replace(path, icon){
 | |
|         return cache.get(cache.FILE_PATH, dirname(path), false)
 | |
|             .then((res) => {
 | |
|                 if(!res) return Promise.resolve();
 | |
|                 let files = res.results.map((file) => {
 | |
|                     if(file.name === basename(path)){
 | |
|                         if(!icon){
 | |
|                             delete file.icon;
 | |
|                             delete file._timestamp;
 | |
|                         }
 | |
|                         if(icon){
 | |
|                             file.icon = icon;
 | |
|                             file._timestamp = (new Date()).getTime();
 | |
|                         }
 | |
|                     }
 | |
|                     return file;
 | |
|                 });
 | |
|                 res.results = files;
 | |
|                 return cache.put(cache.FILE_PATH, dirname(path), res)
 | |
|                     .then((res) => this._ls_from_cache(dirname(path)));
 | |
|             });
 | |
|     }
 | |
| 
 | |
|     _add(path, icon){
 | |
|         return cache.get(cache.FILE_PATH, dirname(path), false)
 | |
|             .then((res) => {
 | |
|                 if(!res) return Promise.resolve();
 | |
|                 let file = {
 | |
|                     name: basename(path),
 | |
|                     type: /\/$/.test(path) ? 'directory' : 'file',
 | |
|                     _timestamp: (new Date()).getTime()
 | |
|                 };
 | |
|                 if(icon) file.icon = icon;
 | |
|                 res.results.push(file);
 | |
|                 return cache.put(cache.FILE_PATH, dirname(path), res)
 | |
|                     .then((res) => this._ls_from_cache(dirname(path)));
 | |
|             });
 | |
|     }
 | |
|     _remove(path){
 | |
|         return cache.get(cache.FILE_PATH, dirname(path), false)
 | |
|             .then((res) => {
 | |
|                 if(!res) return Promise.resolve();
 | |
|                 let files = res.results.filter((file) => {
 | |
|                     return file.name === basename(path) ? false : true;
 | |
|                 });
 | |
|                 res.results = files;
 | |
|                 return cache.put(cache.FILE_PATH, dirname(path), res)
 | |
|                     .then((res) => this._ls_from_cache(dirname(path)));
 | |
|             });
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| export const Files = new FileSystem();
 | |
| window.Files = Files;
 | 
