-
Notifications
You must be signed in to change notification settings - Fork 31
/
MirrorConfig.js
155 lines (141 loc) · 6.98 KB
/
MirrorConfig.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
const debug = require('debug')('dweb-mirror:MirrorConfig');
const forever = require('async/forever');
const { ObjectDeeperAssign } = require('@internetarchive/dweb-archivecontroller');
const ConfigController = require('./ConfigController');
const CrawlManager = require('./CrawlManager');
class MirrorConfig extends ConfigController {
/*
Subclass of ConfigController specific to mirroring
*/
constructor(...objs) {
super(...objs);
}
static initializeUserConfig(cb) {
// Return user configuration, initializing if required.
this.initializeUserConfigFile(this.userConfigFile, this.defaultUserConfig, cb);
}
static new(filenames, setState, cb) {
// filenames Optional list of filenames for configation otherwies uses defaultConfigFiles
// setState({directories})
if (typeof filenames === 'function') { cb = filenames; filenames = undefined; }
if (!(filenames && filenames.length)) { filenames = this.defaultConfigFiles; } // Doesnt include userConfigFile
super.new(filenames, (err, config) => {
if (!err) config.setupPeriodically(setState); // Periodically - rescan Directories;
cb(err, config);
});
}
resolveDirectories(setState) {
// TODO note this could be slow - it uses glob.sync - see TODO in ConfigController.resolves
// Handle ~/ ./ ../ and expand * or ?? etc
// setState({directories}) optional
const newDirectories = ConfigController.resolves(
this.configOpts.filter(c => c.directories).pop().directories // Find last one to define directories
);
if (!Array.isArray(this.directories)) this.directories = []; // Handle start when its undefined
const adding = newDirectories.filter(d => !this.directories.includes(d));
const removing = this.directories.filter(d => !newDirectories.includes(d));
if (adding.length || removing.length) {
if (adding.length) debug('Adding directories %s', adding.join('; '));
if (removing.length) debug('Removing directories %s', removing.join('; '));
this.directories = newDirectories;
if (setState) setState({ directories: this.directories });
}
}
setOpts(...opts) {
// Extend base class to handle specific derivations of opts
const oldDirectories = this.directories; // Save old directories
super.setOpts(...opts); // Just combined and store ops
// Note at this point this.directories will be set to all of them, which is not what we want.
// Remove first so that resolveDirectories will report what its actually using
this.directories = oldDirectories; // and restore as actually want only resolv
this.resolveDirectories(); // Handle ~/ ./ ../ and expand * or ?? etc
['archiveui', 'bookreader', 'epubreader', 'nodemodules']
.map(d => this[d])
.forEach(o => o.directory = ConfigController.firstExisting(o.directories)); // Handle ~/ ./ ../ * ?? and find first match
}
setupPeriodically(setState) {
// Re-resolve the directories options to see if its changed
// if changed will update mfs
if (this.rescanDirectories) {
forever((next) => setTimeout(() => {
this.resolveDirectories(setState);
next();
}, this.rescanDirectories * 1000));
}
}
setAndWriteUser(obj, cb) {
// Set the configuration in the ConfigManager, and write to user file
this.setAndWriteUserFile(MirrorConfig.userConfigFile, obj, cb);
}
writeUser(cb) {
// Write user configuration to file
this.writeUserFile(MirrorConfig.userConfigFile, cb);
}
deleteUserTask({ identifier, query }) {
// Remove task for identifier (handles multi-identifier tasks correctly)
const task = this.findTask({ identifier });
if (task) {
if (Array.isArray(task.identifier) && (task.identifier.length > 1)) {
task.identifier.splice(task.identifier.indexOf(identifier), 1); // Old task - remove identifier
} else { // Single identifier or array length=1
this.apps.crawl.tasks.splice(this.apps.crawl.tasks.indexOf(task), 1);
}
}
}
writeUserTaskLevel({ identifier, level, query }, cb) {
// Update, or create a new task for an identifier (handles multi-identifier tasks correctly)
if (level === 'none') {
this.deleteUserTask({ identifier, query });
} else {
let task = this.findTask({ identifier, query });
if (!task) {
ObjectDeeperAssign(this, { apps: { crawl: {} } });
if (!this.apps.crawl.tasks) {
this.apps.crawl.tasks = [];
}
task = Object.assign({}, identifier ? { identifier } : null, query ? { query } : null);
this.apps.crawl.tasks.push(task);
} else if (Array.isArray(task.identifier) && (task.identifier.length > 1)) {
task.identifier.splice(task.identifier.indexOf(identifier), 1); // Old task - remove identifier
task = Object.assign({}, task, { identifier }); // New task for just this identifier
this.apps.crawl.tasks.push(task);
}
// By this point this.apps.crawl.tasks[] should have a task {identifier}, possibly with old state i.e. findTask({identifier}) would now succeed
task.level = level; // Only change level of that task
}
this.writeUser(cb); // And write back current state
}
findTask({ identifier, query }) {
// Find and return task from config
return this.apps.crawl.tasks.find(t => (identifier && t.identifier && t.identifier.includes(identifier)) || (query && t.query === query));
}
/**
* Find any task and return crawlInfo (which is the task)
* @param identifier
* @param query
* @param mediatype
* @returns {identifier, query, search, related } // A task object as in the config.apps.crawl.tasks
*/
crawlInfo({ identifier = undefined, query = undefined, mediatype = undefined }) {
/*
Check if member being crawled and return info suitable for adding into ArchiveMember and usable by the UI
*/
let task = this.findTask({ identifier, query });
if (!task) {
task = {};
} else {
const isDetailsOrMore = CrawlManager._levels.indexOf(task.level) >= CrawlManager._levels.indexOf('details');
const isSearch = query || (mediatype === 'collection'); // TODO-UXLOCAL need to catch searches (which do not use regular identifiers)
task.search = task.search || (isDetailsOrMore && isSearch && this.apps.crawl.opts.defaultDetailsSearch);
}
return task;
}
}
MirrorConfig.userConfigFile = '~/dweb-mirror.config.yaml'; // contents overwritten by writeUser or setAndWriteUser
// Default to just top 30 tiles of home page
// Default configuration for user file
MirrorConfig.defaultUserConfig = { apps: { crawl: { tasks: [{ identifier: ['home'], level: 'details', search: [{ sort: '-downloads', rows: 30, level: 'tile' }] }] } } };
// config files (later override earlier) note the userConfigFile is always appended
// If this is ever more than one file in defaultConfigFiles then the code in dweb-archive that for statusFromConfig will need editing as assumes userConfigFile returned in position 1
MirrorConfig.defaultConfigFiles = ['./configDefaults.yaml'];
exports = module.exports = MirrorConfig;