mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-05-22 12:33:40 +08:00
299 lines
8.3 KiB
JavaScript
299 lines
8.3 KiB
JavaScript
var fs = require('fs')
|
|
, path = require('path')
|
|
, util = require('util')
|
|
, wrench = require('wrench')
|
|
, Resource = require('../resource')
|
|
, Collection = require('./collection')
|
|
, UserCollection = require('./user-collection')
|
|
, Files = require('./files')
|
|
, Dashboard = require('./dashboard')
|
|
, ClientLib = require('./client-lib')
|
|
, debug = require('debug')('internal-resources')
|
|
, path = require('path')
|
|
, loadTypes = require('../type-loader')
|
|
, uuid = require('../util/uuid')
|
|
, sh = require('shelljs');
|
|
|
|
/*!
|
|
* Builds a list of resources from config
|
|
*
|
|
* @param {Array of Object} resourceConfig
|
|
* @param {Server} server
|
|
* @returns {Array of Resource} resources
|
|
*/
|
|
|
|
|
|
function InternalResources(settings, server) {
|
|
settings.configPath = settings.configPath || './';
|
|
Resource.apply(this, arguments);
|
|
this.server = server;
|
|
// internal resource
|
|
this.internal = true;
|
|
debug('constructed');
|
|
}
|
|
util.inherits(InternalResources, Resource);
|
|
module.exports = InternalResources;
|
|
|
|
var excludedTypes = {
|
|
Dashboard: 1,
|
|
Files: 1,
|
|
ClientLib: 1,
|
|
InternalResources: 1
|
|
};
|
|
|
|
InternalResources.prototype.handle = function(ctx, next) {
|
|
|
|
if (!ctx.req.isRoot) {
|
|
ctx.done({statusCode: 401, message: "Not Allowed"});
|
|
return;
|
|
}
|
|
|
|
var basepath = this.config.configPath;
|
|
|
|
// TODO handle file system ENOENT, send all else to ctx.done
|
|
// if(err) return ctx.done(err);
|
|
|
|
var resource
|
|
, id = ctx.url && ctx.url.replace('/', '')
|
|
, file;
|
|
|
|
if(ctx.url === '/types') {
|
|
loadTypes(function(defaults, types) {
|
|
Object.keys(types).forEach(function(key) {
|
|
defaults[key] = types[key];
|
|
});
|
|
Object.keys(defaults).forEach(function(key) {
|
|
if(excludedTypes[key]) return;
|
|
var c = defaults[key]
|
|
, pages = c.dashboard && c.dashboard.pages;
|
|
|
|
if (!pages && c.events) {
|
|
pages = ['Config', 'Events'];
|
|
}
|
|
|
|
defaults[key] = {
|
|
type: c.name,
|
|
defaultPath: c.defaultPath,
|
|
label: c.label,
|
|
dashboardPages: pages
|
|
};
|
|
});
|
|
ctx.done(null, defaults);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(ctx.req.method != 'GET' && ctx.server) {
|
|
// clear cache
|
|
delete ctx.server.__resourceCache;
|
|
}
|
|
|
|
switch (ctx.req.method) {
|
|
case 'POST':
|
|
case 'PUT':
|
|
var parts
|
|
, fileName
|
|
, isJson;
|
|
|
|
resource = ctx.body;
|
|
|
|
parts = (ctx.url || '').split('/').filter(function(p) { return p; });
|
|
|
|
if (!parts || !parts.length) return ctx.done({statusCode: 400, message: "You must provide a resource"});
|
|
|
|
id = parts[0];
|
|
if (!parts[1] || parts[parts.length - 1].indexOf('.') === -1) {
|
|
parts.push('config.json');
|
|
}
|
|
file = path.join(basepath, 'resources', parts.join('/'));
|
|
|
|
fileName = parts[parts.length - 1].toLowerCase();
|
|
isJson = fileName.lastIndexOf('.json') === fileName.length - 5;
|
|
|
|
fs.stat(path.join(basepath, 'resources', id), function(err, stat) {
|
|
if (!stat || !stat.isDirectory()) {
|
|
fs.mkdir(path.join(basepath, 'resources', id), function(err) {
|
|
if (err) return ctx.done(err);
|
|
save();
|
|
});
|
|
} else {
|
|
save();
|
|
}
|
|
});
|
|
|
|
function save() {
|
|
if (isJson && !resource.$setAll) {
|
|
fs.readFile(file, 'utf-8', function(err, currentValue) {
|
|
if (!currentValue) {
|
|
writeFile(resource);
|
|
} else {
|
|
try {
|
|
var currentJson = JSON.parse(currentValue);
|
|
Object.keys(resource).forEach(function(k) {
|
|
currentJson[k] = resource[k];
|
|
});
|
|
writeFile(currentJson);
|
|
} catch (ex) {
|
|
writeFile(resource);
|
|
}
|
|
}
|
|
});
|
|
} else if (isJson) {
|
|
delete resource.$setAll;
|
|
writeFile(resource);
|
|
} else {
|
|
writeFile(resource.value);
|
|
}
|
|
}
|
|
|
|
function writeFile(value) {
|
|
var newId = value.id;
|
|
delete value.id;
|
|
if (typeof value === 'object') value = JSON.stringify(value, null, '\t');
|
|
notifyType(id, 'Changed', resource, ctx.server, function() {
|
|
fs.writeFile(file, value, function(err) {
|
|
if (err) return ctx.done(err);
|
|
|
|
if (fileName.lastIndexOf('config.json') === fileName.length - ('config.json').length &&
|
|
newId && newId !== id) {
|
|
//rename
|
|
if (newId.indexOf('/') === 0) newId = newId.substring(1);
|
|
fs.rename(path.join(basepath, 'resources', id), path.join(basepath, 'resources', newId), function(err) {
|
|
if (err) return ctx.done(err);
|
|
resource.id = newId;
|
|
ctx.done(null, resource);
|
|
});
|
|
} else {
|
|
resource.id = id;
|
|
ctx.done(null, resource);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
break;
|
|
case 'GET':
|
|
if(id) {
|
|
isJson = undefined;
|
|
file = id;
|
|
if (id.lastIndexOf('.') <= id.lastIndexOf('/')) {
|
|
file += '/config.json';
|
|
}
|
|
isJson = file.lastIndexOf('.json') === (file.length - ('.json').length);
|
|
|
|
file = path.join(basepath, 'resources', file);
|
|
fs.readFile(file, 'utf-8', function(err, value) {
|
|
var json;
|
|
if (!value) return next();
|
|
if (isJson) {
|
|
try {
|
|
json = JSON.parse(value);
|
|
json.id = id;
|
|
ctx.done(null, json);
|
|
} catch (ex) {
|
|
ctx.done("Error parsing file: " + ex.message);
|
|
}
|
|
} else {
|
|
ctx.done(null, {value: value});
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
fs.readdir(path.join(basepath, 'resources'), function(err, folders) {
|
|
var remaining
|
|
, fileDone
|
|
, resources = [];
|
|
if (!folders) return ctx.done(err);
|
|
remaining = folders.length;
|
|
fileDone = function() {
|
|
remaining--;
|
|
if (remaining <= 0) {
|
|
resources = resources.sort(function(a, b) {
|
|
var sort = a.__ctime - b.__ctime;
|
|
if (sort === 0) {
|
|
sort = a.id.localeCompare(b.id);
|
|
}
|
|
return sort;
|
|
}).map(function(r) {
|
|
delete r.__ctime;
|
|
return r;
|
|
});
|
|
ctx.done(null, resources);
|
|
}
|
|
};
|
|
|
|
if (!remaining) {
|
|
ctx.done(null, []);
|
|
}
|
|
|
|
folders.forEach(function(f) {
|
|
var fullPath = path.join(basepath, 'resources', f, 'config.json');
|
|
fs.stat(fullPath, function(err, stat) {
|
|
if (stat) {
|
|
fs.readFile(fullPath, 'utf-8', function(err, value) {
|
|
var json;
|
|
if (!value) return fileDone();
|
|
try {
|
|
json = JSON.parse(value);
|
|
json.id = f;
|
|
json.__ctime = stat.ctime;
|
|
resources.push(json);
|
|
fileDone();
|
|
} catch (ex) {
|
|
ctx.done("Error parsing config.json: " + ex.message);
|
|
}
|
|
});
|
|
} else {
|
|
fileDone();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
break;
|
|
case 'DELETE':
|
|
if (!id) return ctx.done("You must specify a resource");
|
|
var resourcePath = path.join(basepath, 'resources', id);
|
|
wrench.rmdirRecursive(resourcePath, function(err) {
|
|
if (err) return ctx.done(err);
|
|
notifyType(id, 'Deleted', null, ctx.server, function() {
|
|
ctx.done();
|
|
});
|
|
});
|
|
break;
|
|
default:
|
|
next();
|
|
}
|
|
};
|
|
|
|
function notifyType(id, event, config, server, fn) {
|
|
if(server) {
|
|
var found = false;
|
|
server.resources.forEach(function(r) {
|
|
if(id === r.name) {
|
|
found = true;
|
|
debug('notifying resource', r.config.path);
|
|
if (r['config' + event]) {
|
|
r['config' + event](config, function(err) {
|
|
if (err) return fn(err);
|
|
r.config = config;
|
|
fn();
|
|
r.emit(event.toLowerCase(), config);
|
|
});
|
|
} else {
|
|
fn();
|
|
r.emit(event.toLowerCase(), config);
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
if (!found) {
|
|
fn();
|
|
}
|
|
} else {
|
|
fn();
|
|
}
|
|
}
|