2017-01-31 07:50:28 +01:00
|
|
|
const StorageBase = require('./storage-base');
|
|
|
|
const Launcher = require('../comp/launcher');
|
2015-12-02 21:39:40 +01:00
|
|
|
|
2017-01-31 07:50:28 +01:00
|
|
|
const fileWatchers = {};
|
2016-02-06 11:59:57 +01:00
|
|
|
|
2017-01-31 07:50:28 +01:00
|
|
|
const StorageFile = StorageBase.extend({
|
2015-12-02 21:39:40 +01:00
|
|
|
name: 'file',
|
2016-03-12 12:22:35 +01:00
|
|
|
icon: 'hdd-o',
|
2015-12-02 21:39:40 +01:00
|
|
|
enabled: !!Launcher,
|
2016-03-13 17:08:25 +01:00
|
|
|
system: true,
|
2016-08-21 18:07:59 +02:00
|
|
|
backup: true,
|
2015-12-02 21:39:40 +01:00
|
|
|
|
2016-03-12 17:49:52 +01:00
|
|
|
load: function(path, opts, callback) {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Load', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
2017-02-01 02:37:43 +01:00
|
|
|
|
|
|
|
const onError = e => {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.error('Error reading local file', path, e);
|
2017-02-01 02:37:43 +01:00
|
|
|
if (callback) {
|
|
|
|
callback(e, null);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-02-05 11:08:26 +01:00
|
|
|
Launcher.readFile(path, undefined, (data, err) => {
|
|
|
|
if (err) {
|
|
|
|
return onError(err);
|
|
|
|
}
|
|
|
|
Launcher.statFile(path, (stat, err) => {
|
|
|
|
if (err) {
|
|
|
|
return onError(err);
|
|
|
|
}
|
2017-02-01 02:37:43 +01:00
|
|
|
const rev = stat.mtime.getTime().toString();
|
|
|
|
this.logger.debug('Loaded', path, rev, this.logger.ts(ts));
|
|
|
|
if (callback) {
|
|
|
|
callback(null, data.buffer, { rev: rev });
|
|
|
|
}
|
2017-02-05 11:08:26 +01:00
|
|
|
});
|
|
|
|
});
|
2015-12-02 21:39:40 +01:00
|
|
|
},
|
|
|
|
|
2016-03-12 17:49:52 +01:00
|
|
|
stat: function(path, opts, callback) {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Stat', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
2017-02-01 02:37:43 +01:00
|
|
|
|
2017-02-05 11:49:22 +01:00
|
|
|
Launcher.statFile(path, (stat, err) => {
|
|
|
|
if (err) {
|
|
|
|
this.logger.error('Error stat local file', path, err);
|
|
|
|
if (err.code === 'ENOENT') {
|
|
|
|
err.notFound = true;
|
|
|
|
}
|
|
|
|
return callback && callback(err, null);
|
|
|
|
}
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Stat done', path, this.logger.ts(ts));
|
2017-02-01 02:37:43 +01:00
|
|
|
if (callback) {
|
|
|
|
const fileRev = stat.mtime.getTime().toString();
|
|
|
|
callback(null, { rev: fileRev });
|
|
|
|
}
|
|
|
|
});
|
2016-02-06 11:59:57 +01:00
|
|
|
},
|
|
|
|
|
2016-03-12 17:49:52 +01:00
|
|
|
save: function(path, opts, data, callback, rev) {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Save', path, rev);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
2017-02-01 02:37:43 +01:00
|
|
|
|
|
|
|
const onError = e => {
|
2019-08-16 21:36:22 +02:00
|
|
|
if (Object.prototype.hasOwnProperty.call(e, 'code') && e.code === 'EISDIR') {
|
2019-01-03 08:18:13 +01:00
|
|
|
e.isDir = true;
|
|
|
|
}
|
2017-02-01 02:37:43 +01:00
|
|
|
this.logger.error('Error writing local file', path, e);
|
|
|
|
if (callback) {
|
|
|
|
callback(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const write = () => {
|
2017-02-05 11:08:26 +01:00
|
|
|
Launcher.writeFile(path, data, err => {
|
|
|
|
if (err) {
|
|
|
|
return onError(err);
|
|
|
|
}
|
|
|
|
Launcher.statFile(path, (stat, err) => {
|
|
|
|
if (err) {
|
|
|
|
return onError(err);
|
|
|
|
}
|
2017-02-03 00:48:20 +01:00
|
|
|
const newRev = stat.mtime.getTime().toString();
|
2017-02-01 02:37:43 +01:00
|
|
|
this.logger.debug('Saved', path, this.logger.ts(ts));
|
|
|
|
if (callback) {
|
|
|
|
callback(undefined, { rev: newRev });
|
|
|
|
}
|
2017-02-05 11:08:26 +01:00
|
|
|
});
|
|
|
|
});
|
2017-02-01 02:37:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if (rev) {
|
2017-02-05 11:49:22 +01:00
|
|
|
Launcher.statFile(path, (stat, err) => {
|
|
|
|
if (err) {
|
2017-02-21 20:02:39 +01:00
|
|
|
return write();
|
2017-02-05 11:49:22 +01:00
|
|
|
}
|
2017-02-03 00:48:20 +01:00
|
|
|
const fileRev = stat.mtime.getTime().toString();
|
2017-02-01 02:37:43 +01:00
|
|
|
if (fileRev !== rev) {
|
|
|
|
this.logger.debug('Save mtime differs', rev, fileRev);
|
2017-02-05 11:49:22 +01:00
|
|
|
return callback && callback({ revConflict: true }, { rev: fileRev });
|
2016-02-06 11:59:57 +01:00
|
|
|
}
|
2017-02-01 02:37:43 +01:00
|
|
|
write();
|
2017-02-05 11:49:22 +01:00
|
|
|
});
|
2017-02-01 02:37:43 +01:00
|
|
|
} else {
|
|
|
|
write();
|
2015-12-02 21:39:40 +01:00
|
|
|
}
|
2016-02-06 11:59:57 +01:00
|
|
|
},
|
|
|
|
|
2016-08-21 18:07:59 +02:00
|
|
|
mkdir: function(path, callback) {
|
|
|
|
this.logger.debug('Make dir', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
2017-02-01 02:37:43 +01:00
|
|
|
|
2017-02-05 23:34:37 +01:00
|
|
|
Launcher.mkdir(path, err => {
|
|
|
|
if (err) {
|
|
|
|
this.logger.error('Error making local dir', path, err);
|
|
|
|
if (callback) { callback('Error making local dir'); }
|
|
|
|
} else {
|
|
|
|
this.logger.debug('Made dir', path, this.logger.ts(ts));
|
|
|
|
if (callback) { callback(); }
|
2017-02-01 02:37:43 +01:00
|
|
|
}
|
2017-02-05 23:34:37 +01:00
|
|
|
});
|
2016-08-21 18:07:59 +02:00
|
|
|
},
|
|
|
|
|
2016-02-06 11:59:57 +01:00
|
|
|
watch: function(path, callback) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const names = Launcher.parsePath(path);
|
2016-02-06 11:59:57 +01:00
|
|
|
if (!fileWatchers[names.dir]) {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Watch dir', names.dir);
|
2017-01-31 07:50:28 +01:00
|
|
|
const fsWatcher = Launcher.createFsWatcher(names.dir);
|
2017-02-04 15:31:21 +01:00
|
|
|
if (fsWatcher) {
|
|
|
|
fsWatcher.on('change', this.fsWatcherChange.bind(this, names.dir));
|
|
|
|
fileWatchers[names.dir] = {
|
|
|
|
fsWatcher: fsWatcher,
|
|
|
|
callbacks: []
|
|
|
|
};
|
|
|
|
}
|
2016-02-06 11:59:57 +01:00
|
|
|
}
|
2017-02-04 15:31:21 +01:00
|
|
|
|
|
|
|
const fsWatcher = fileWatchers[names.dir];
|
2017-02-05 11:49:22 +01:00
|
|
|
if (fsWatcher) {
|
|
|
|
fsWatcher.callbacks.push({
|
|
|
|
file: names.file,
|
|
|
|
callback: callback
|
|
|
|
});
|
|
|
|
}
|
2016-02-06 11:59:57 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
unwatch: function(path) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const names = Launcher.parsePath(path);
|
|
|
|
const watcher = fileWatchers[names.dir];
|
2016-02-06 11:59:57 +01:00
|
|
|
if (watcher) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const ix = watcher.callbacks.findIndex(cb => cb.file === names.file);
|
2016-02-06 11:59:57 +01:00
|
|
|
if (ix >= 0) {
|
|
|
|
watcher.callbacks.splice(ix, 1);
|
|
|
|
}
|
|
|
|
if (!watcher.callbacks.length) {
|
2016-03-27 09:06:23 +02:00
|
|
|
this.logger.debug('Stop watch dir', names.dir);
|
2017-02-05 01:12:01 +01:00
|
|
|
watcher.fsWatcher.close();
|
2016-02-06 11:59:57 +01:00
|
|
|
delete fileWatchers[names.dir];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
fsWatcherChange: function(dirname, evt, fileName) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const watcher = fileWatchers[dirname];
|
2016-02-06 11:59:57 +01:00
|
|
|
if (watcher) {
|
2016-07-17 13:30:38 +02:00
|
|
|
watcher.callbacks.forEach(cb => {
|
2016-02-06 11:59:57 +01:00
|
|
|
if (cb.file === fileName && typeof cb.callback === 'function') {
|
2016-07-17 13:30:38 +02:00
|
|
|
this.logger.debug('File changed', dirname, evt, fileName);
|
2016-02-06 11:59:57 +01:00
|
|
|
cb.callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-12-02 21:39:40 +01:00
|
|
|
}
|
2016-03-27 09:06:23 +02:00
|
|
|
});
|
2015-12-02 21:39:40 +01:00
|
|
|
|
2016-03-27 09:46:43 +02:00
|
|
|
module.exports = new StorageFile();
|