1 | 'use strict';
|
2 |
|
3 | const Promise = require('bluebird');
|
4 | const Mime = require('mime');
|
5 | const Fs = Promise.promisifyAll(require('fs'), {suffix: 'Promised'});
|
6 | const Path = require('path');
|
7 |
|
8 | const Tools = require('unifile-common-tools');
|
9 |
|
10 | const NAME = 'fs';
|
11 |
|
12 | const validatePath = Symbol('validatePath');
|
13 |
|
14 | function statToFileInfos(filename, stat) {
|
15 | const isDir = stat.isDirectory();
|
16 | return {
|
17 | size: stat.size,
|
18 | modified: stat.mtime,
|
19 | name: filename,
|
20 | isDir: isDir,
|
21 | mime: isDir ? 'application/directory' : Mime.getType(filename)
|
22 | };
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | class FsConnector {
|
29 | |
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | constructor(config) {
|
41 | const conf = config || {};
|
42 | this.showHiddenFile = conf.showHiddenFile;
|
43 | this.infos = Tools.mergeInfos(conf.infos, {
|
44 | name: NAME,
|
45 | displayName: 'Local',
|
46 | icon: '',
|
47 | description: 'Edit files on your local drive.'
|
48 | });
|
49 | this.name = this.infos.name;
|
50 |
|
51 | if(!conf.sandbox) this.sandbox = [];
|
52 | else if(conf.sandbox.constructor === String) this.sandbox = [conf.sandbox];
|
53 | else if(Array.isArray(conf.sandbox)) this.sandbox = conf.sandbox;
|
54 | else throw new Error('Invalid sandbox path. Must be a string or an array');
|
55 |
|
56 | if(conf.rootPath) this.rootPath = conf.rootPath;
|
57 | else if(this.sandbox.length > 0) this.rootPath = this.sandbox[0];
|
58 | else this.rootPath = '/';
|
59 | }
|
60 |
|
61 | getInfos(session) {
|
62 | return Object.assign({
|
63 | isLoggedIn: true,
|
64 | isOAuth: false,
|
65 | username: process.env.USER
|
66 | }, this.infos);
|
67 | }
|
68 |
|
69 |
|
70 |
|
71 | getAuthorizeURL(session) {
|
72 | return Promise.resolve('');
|
73 | }
|
74 |
|
75 | setAccessToken(session, token) {
|
76 | return Promise.resolve(token);
|
77 | }
|
78 |
|
79 | clearAccessToken(session) {
|
80 | return Promise.resolve();
|
81 | }
|
82 |
|
83 | login(session, loginInfos) {
|
84 | return new Promise.resolve();
|
85 | }
|
86 |
|
87 |
|
88 |
|
89 | readdir(session, path) {
|
90 | try {
|
91 | const securePath = this[validatePath](path);
|
92 | return Fs.readdirPromised(securePath)
|
93 | .reduce((memo, entry) => {
|
94 | if(this.showHiddenFile || entry.charAt(0) != '.') {
|
95 | return Fs.statPromised(Path.resolve(securePath, entry))
|
96 | .then((stat) => {
|
97 | memo.push(statToFileInfos(entry, stat));
|
98 | return memo;
|
99 | });
|
100 | } else {
|
101 | return memo;
|
102 | }
|
103 | }, []);
|
104 | } catch (e) {
|
105 | return Promise.reject(e);
|
106 | }
|
107 | }
|
108 |
|
109 | stat(session, path) {
|
110 | try {
|
111 | const securePath = this[validatePath](path);
|
112 | return Fs.statPromised(securePath)
|
113 | .then((stat) => {
|
114 | return statToFileInfos(Path.basename(securePath), stat);
|
115 | });
|
116 | } catch (e) {
|
117 | return Promise.reject(e);
|
118 | }
|
119 | }
|
120 |
|
121 | mkdir(session, path) {
|
122 | try {
|
123 | return Fs.mkdirPromised(this[validatePath](path));
|
124 | } catch (e) {
|
125 | return Promise.reject(e);
|
126 | }
|
127 | }
|
128 |
|
129 | writeFile(session, path, data) {
|
130 | try {
|
131 | return Fs.writeFilePromised(this[validatePath](path), data);
|
132 | } catch (e) {
|
133 | return Promise.reject(e);
|
134 | }
|
135 | }
|
136 |
|
137 | createWriteStream(session, path) {
|
138 | return Fs.createWriteStream(this[validatePath](path));
|
139 | }
|
140 |
|
141 | readFile(session, path) {
|
142 | try {
|
143 | return Fs.readFilePromised(this[validatePath](path));
|
144 | } catch (e) {
|
145 | return Promise.reject(e);
|
146 | }
|
147 | }
|
148 |
|
149 | createReadStream(session, path) {
|
150 | return Fs.createReadStream(this[validatePath](path), {encoding: 'utf8'});
|
151 | }
|
152 |
|
153 | rename(session, src, dest) {
|
154 | try {
|
155 | return Fs.renamePromised(this[validatePath](src), this[validatePath](dest));
|
156 | } catch (e) {
|
157 | return Promise.reject(e);
|
158 | }
|
159 | }
|
160 |
|
161 | unlink(session, path) {
|
162 | try {
|
163 | return Fs.unlinkPromised(this[validatePath](path));
|
164 | } catch (e) {
|
165 | return Promise.reject(e);
|
166 | }
|
167 | }
|
168 |
|
169 | rmdir(session, path) {
|
170 | try {
|
171 | return Fs.rmdirPromised(this[validatePath](path));
|
172 | } catch (e) {
|
173 | return Promise.reject(e);
|
174 | }
|
175 | }
|
176 |
|
177 | batch(session, actions, message) {
|
178 | return Tools.simpleBatch(this, session, actions);
|
179 | }
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 | [validatePath](path) {
|
186 | const absolutePath = Path.resolve(this.rootPath, path);
|
187 | if(this.sandbox.length !== 0 && !this.sandbox.some((sandboxPath) => absolutePath.startsWith(sandboxPath)))
|
188 | throw new Error(`Path is out of the sandbox: ${absolutePath}`);
|
189 | return absolutePath;
|
190 | }
|
191 | }
|
192 |
|
193 | module.exports = FsConnector;
|