Introduce ESLint and Prettier
This commit is contained in:
1 parent
9c943d0bbf
commit
e494bdca8a
26 files changed
+1585
-1594
No files matched your search
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "script",
|
||||||
|
"ecmaVersion": 8
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-shadow": ["error", { "allow": ["err"] }],
|
||||||
|
"handle-callback-err": "error",
|
||||||
|
"prefer-arrow-callback": "error",
|
||||||
|
"curly": ["error", "multi-or-nest", "consistent"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,3 +16,5 @@ config/ext
|
|||||||
|
|
||||||
debug/*
|
debug/*
|
||||||
!debug/.gitkeep
|
!debug/.gitkeep
|
||||||
|
|
||||||
|
.eslintrc.json
|
||||||
+199
-136
@@ -29,8 +29,7 @@ const auth = require("./auth");
|
|||||||
const cache = require("./cache");
|
const cache = require("./cache");
|
||||||
|
|
||||||
function udpConReq(address, un, key, callback) {
|
function udpConReq(address, un, key, callback) {
|
||||||
if (!address)
|
if (!address) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
let [host, port] = address.split(":", 2);
|
let [host, port] = address.split(":", 2);
|
||||||
|
|
||||||
@@ -38,18 +37,22 @@ function udpConReq(address, un, key, callback) {
|
|||||||
port = 80;
|
port = 80;
|
||||||
} else {
|
} else {
|
||||||
port = parseInt(port);
|
port = parseInt(port);
|
||||||
if (isNaN(port))
|
if (isNaN(port)) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ts = Math.trunc(Date.now() / 1000);
|
const ts = Math.trunc(Date.now() / 1000);
|
||||||
const id = Math.trunc(Math.random() * 4294967295);
|
const id = Math.trunc(Math.random() * 4294967295);
|
||||||
const cn = crypto.randomBytes(8).toString("hex");
|
const cn = crypto.randomBytes(8).toString("hex");
|
||||||
const sig = crypto.createHmac("sha1", key).update(`${ts}${id}${un}${cn}`).digest("hex");
|
const sig = crypto
|
||||||
|
.createHmac("sha1", key)
|
||||||
|
.update(`${ts}${id}${un}${cn}`)
|
||||||
|
.digest("hex");
|
||||||
const uri = `http://${address}?ts=${ts}&id=${id}&un=${un}&cn=${cn}&sig=${sig}`;
|
const uri = `http://${address}?ts=${ts}&id=${id}&un=${un}&cn=${cn}&sig=${sig}`;
|
||||||
const message = Buffer.from(`GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`);
|
const message = Buffer.from(
|
||||||
|
`GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`
|
||||||
|
);
|
||||||
|
|
||||||
const client = dgram.createSocket({type: "udp4", reuseAddr: true});
|
const client = dgram.createSocket({ type: "udp4", reuseAddr: true });
|
||||||
const UDP_CONNECTION_REQUEST_PORT = config.get("UDP_CONNECTION_REQUEST_PORT");
|
const UDP_CONNECTION_REQUEST_PORT = config.get("UDP_CONNECTION_REQUEST_PORT");
|
||||||
|
|
||||||
if (UDP_CONNECTION_REQUEST_PORT)
|
if (UDP_CONNECTION_REQUEST_PORT)
|
||||||
@@ -59,10 +62,10 @@ function udpConReq(address, un, key, callback) {
|
|||||||
// are allowed to bind to the same address and port. The STUN server needs
|
// are allowed to bind to the same address and port. The STUN server needs
|
||||||
// to open its UDP port with the SO_REUSEADDR option, allowing the NBI to
|
// to open its UDP port with the SO_REUSEADDR option, allowing the NBI to
|
||||||
// also bind to the same port.
|
// also bind to the same port.
|
||||||
client.bind({port: UDP_CONNECTION_REQUEST_PORT, exclusive: true});
|
client.bind({ port: UDP_CONNECTION_REQUEST_PORT, exclusive: true });
|
||||||
|
|
||||||
let count = 3;
|
let count = 3;
|
||||||
const func = (err) => {
|
const func = err => {
|
||||||
if (err || --count <= 0) {
|
if (err || --count <= 0) {
|
||||||
client.close();
|
client.close();
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@@ -73,11 +76,18 @@ function udpConReq(address, un, key, callback) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function httpConReq(url, username, password, allowBasicAuth, timeout, callback) {
|
function httpConReq(
|
||||||
|
url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
allowBasicAuth,
|
||||||
|
timeout,
|
||||||
|
callback
|
||||||
|
) {
|
||||||
const options = URL.parse(url);
|
const options = URL.parse(url);
|
||||||
if (options.protocol !== "http:")
|
if (options.protocol !== "http:")
|
||||||
return callback(new Error("Invalid connection request URL or protocol"));
|
return callback(new Error("Invalid connection request URL or protocol"));
|
||||||
options.agent = new http.Agent({maxSockets: 1});
|
options.agent = new http.Agent({ maxSockets: 1 });
|
||||||
|
|
||||||
function statusToError(statusCode) {
|
function statusToError(statusCode) {
|
||||||
switch (statusCode) {
|
switch (statusCode) {
|
||||||
@@ -93,70 +103,96 @@ function httpConReq(url, username, password, allowBasicAuth, timeout, callback)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = http.get(options, (res) => {
|
const request = http
|
||||||
if (res.statusCode === 401 && res.headers["www-authenticate"]) {
|
.get(options, response => {
|
||||||
const authHeader = auth.parseAuthHeader(res.headers["www-authenticate"]);
|
if (response.statusCode === 401 && response.headers["www-authenticate"]) {
|
||||||
if (authHeader.method === "Basic") {
|
const authHeader = auth.parseAuthHeader(
|
||||||
if (!allowBasicAuth) {
|
response.headers["www-authenticate"]
|
||||||
request.abort();
|
);
|
||||||
return callback(new Error("Basic HTTP authentication not allowed"));
|
if (authHeader.method === "Basic") {
|
||||||
}
|
if (!allowBasicAuth) {
|
||||||
options.headers = {
|
|
||||||
"Authorization": auth.basic(username || "", password || "")
|
|
||||||
};
|
|
||||||
} else if (authHeader.method === "Digest") {
|
|
||||||
options.headers = {
|
|
||||||
"Authorization": auth.digest(username || "", password || "", options.path, "GET", null, authHeader)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = http.get(options, (res) => {
|
|
||||||
if (res.statusCode === 0) {
|
|
||||||
// Workaround for some devices unexpectedly closing the connection
|
|
||||||
request = http.get(options, (res) => {
|
|
||||||
callback(statusToError(res.statusCode));
|
|
||||||
res.resume();
|
|
||||||
}).on("error", () => {
|
|
||||||
request.abort();
|
request.abort();
|
||||||
|
return callback(new Error("Basic HTTP authentication not allowed"));
|
||||||
|
}
|
||||||
|
options.headers = {
|
||||||
|
Authorization: auth.basic(username || "", password || "")
|
||||||
|
};
|
||||||
|
} else if (authHeader.method === "Digest") {
|
||||||
|
options.headers = {
|
||||||
|
Authorization: auth.digest(
|
||||||
|
username || "",
|
||||||
|
password || "",
|
||||||
|
options.path,
|
||||||
|
"GET",
|
||||||
|
null,
|
||||||
|
authHeader
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = http
|
||||||
|
.get(options, res => {
|
||||||
|
if (res.statusCode === 0) {
|
||||||
|
// Workaround for some devices unexpectedly closing the connection
|
||||||
|
let req2;
|
||||||
|
req2 = http
|
||||||
|
.get(options, res2 => {
|
||||||
|
callback(statusToError(res2.statusCode));
|
||||||
|
res2.resume();
|
||||||
|
})
|
||||||
|
.on("error", () => {
|
||||||
|
req2.abort();
|
||||||
|
callback(statusToError(0));
|
||||||
|
})
|
||||||
|
.on("socket", socket => {
|
||||||
|
socket.setTimeout(timeout);
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
req2.abort();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req2();
|
||||||
|
} else {
|
||||||
|
callback(statusToError(res.statusCode));
|
||||||
|
}
|
||||||
|
res.resume();
|
||||||
|
})
|
||||||
|
.on("error", () => {
|
||||||
|
req.abort();
|
||||||
callback(statusToError(0));
|
callback(statusToError(0));
|
||||||
}).on("socket", (socket) => {
|
})
|
||||||
|
.on("socket", socket => {
|
||||||
socket.setTimeout(timeout);
|
socket.setTimeout(timeout);
|
||||||
socket.on("timeout", () => {
|
socket.on("timeout", () => {
|
||||||
request.abort();
|
req.abort();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(statusToError(res.statusCode));
|
callback(statusToError(response.statusCode));
|
||||||
}
|
}
|
||||||
res.resume();
|
// No listener for data so emit resume
|
||||||
}).on("error", () => {
|
response.resume();
|
||||||
request.abort();
|
})
|
||||||
callback(statusToError(0));
|
.on("error", () => {
|
||||||
}).on("socket", (socket) => {
|
|
||||||
socket.setTimeout(timeout);
|
|
||||||
socket.on("timeout", () => {
|
|
||||||
request.abort();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(statusToError(res.statusCode));
|
|
||||||
}
|
|
||||||
// No listener for data so emit resume
|
|
||||||
res.resume();
|
|
||||||
}).on("error", (err) => {
|
|
||||||
request.abort();
|
|
||||||
callback(statusToError(0));
|
|
||||||
}).on("socket", (socket) => {
|
|
||||||
socket.setTimeout(timeout);
|
|
||||||
socket.on("timeout", () => {
|
|
||||||
request.abort();
|
request.abort();
|
||||||
|
callback(statusToError(0));
|
||||||
|
})
|
||||||
|
.on("socket", socket => {
|
||||||
|
socket.setTimeout(timeout);
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
request.abort();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectionRequest(deviceId, callback) {
|
function connectionRequest(deviceId, callback) {
|
||||||
const CONNECTION_REQUEST_TIMEOUT = config.get("CONNECTION_REQUEST_TIMEOUT", deviceId);
|
const CONNECTION_REQUEST_TIMEOUT = config.get(
|
||||||
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = config.get("CONNECTION_REQUEST_ALLOW_BASIC_AUTH", deviceId);
|
"CONNECTION_REQUEST_TIMEOUT",
|
||||||
|
deviceId
|
||||||
|
);
|
||||||
|
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = config.get(
|
||||||
|
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
|
||||||
|
deviceId
|
||||||
|
);
|
||||||
const proj = {
|
const proj = {
|
||||||
"Device.ManagementServer.ConnectionRequestURL._value": 1,
|
"Device.ManagementServer.ConnectionRequestURL._value": 1,
|
||||||
"Device.ManagementServer.UDPConnectionRequestAddress._value": 1,
|
"Device.ManagementServer.UDPConnectionRequestAddress._value": 1,
|
||||||
@@ -168,46 +204,74 @@ function connectionRequest(deviceId, callback) {
|
|||||||
"InternetGatewayDevice.ManagementServer.ConnectionRequestPassword._value": 1
|
"InternetGatewayDevice.ManagementServer.ConnectionRequestPassword._value": 1
|
||||||
};
|
};
|
||||||
|
|
||||||
db.devicesCollection.findOne({_id: deviceId}, proj, (err, device) => {
|
db.devicesCollection.findOne({ _id: deviceId }, proj, (err, device) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
let managementServer, connectionRequestUrl, udpConnectionRequestAddress, username, password;
|
let managementServer,
|
||||||
if (device.Device) // TR-181 data model
|
connectionRequestUrl,
|
||||||
|
udpConnectionRequestAddress,
|
||||||
|
username,
|
||||||
|
password;
|
||||||
|
if (device.Device)
|
||||||
|
// TR-181 data model
|
||||||
managementServer = device.Device.ManagementServer;
|
managementServer = device.Device.ManagementServer;
|
||||||
else // TR-098 data model
|
// TR-098 data model
|
||||||
managementServer = device.InternetGatewayDevice.ManagementServer;
|
else managementServer = device.InternetGatewayDevice.ManagementServer;
|
||||||
|
|
||||||
connectionRequestUrl = managementServer.ConnectionRequestURL._value;
|
connectionRequestUrl = managementServer.ConnectionRequestURL._value;
|
||||||
if (managementServer.UDPConnectionRequestAddress)
|
if (managementServer.UDPConnectionRequestAddress) {
|
||||||
udpConnectionRequestAddress = managementServer.UDPConnectionRequestAddress._value;
|
udpConnectionRequestAddress =
|
||||||
|
managementServer.UDPConnectionRequestAddress._value;
|
||||||
|
}
|
||||||
if (managementServer.ConnectionRequestUsername)
|
if (managementServer.ConnectionRequestUsername)
|
||||||
username = managementServer.ConnectionRequestUsername._value;
|
username = managementServer.ConnectionRequestUsername._value;
|
||||||
if (managementServer.ConnectionRequestPassword)
|
if (managementServer.ConnectionRequestPassword)
|
||||||
password = managementServer.ConnectionRequestPassword._value;
|
password = managementServer.ConnectionRequestPassword._value;
|
||||||
|
|
||||||
const conReq = () => {
|
const conReq = () => {
|
||||||
const udpSent = udpConReq(udpConnectionRequestAddress, username, password, (err) => {
|
const udpSent = udpConReq(
|
||||||
if (err)
|
udpConnectionRequestAddress,
|
||||||
throw err;
|
username,
|
||||||
});
|
password,
|
||||||
|
err => {
|
||||||
|
if (err) throw err;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
httpConReq(connectionRequestUrl, username, password, CONNECTION_REQUEST_ALLOW_BASIC_AUTH, CONNECTION_REQUEST_TIMEOUT, (err) => {
|
httpConReq(
|
||||||
if (udpSent)
|
connectionRequestUrl,
|
||||||
return callback();
|
username,
|
||||||
callback(err);
|
password,
|
||||||
});
|
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
||||||
|
CONNECTION_REQUEST_TIMEOUT,
|
||||||
|
err => {
|
||||||
|
if (udpSent) return callback();
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.auth && config.auth.connectionRequest) {
|
if (config.auth && config.auth.connectionRequest) {
|
||||||
// Callback is optional for backward compatibility
|
// Callback is optional for backward compatibility
|
||||||
if (config.auth.connectionRequest.length > 4)
|
if (config.auth.connectionRequest.length > 4) {
|
||||||
return config.auth.connectionRequest(deviceId, connectionRequestUrl, username, password, (u, p) => {
|
return config.auth.connectionRequest(
|
||||||
username = u;
|
deviceId,
|
||||||
password = p;
|
connectionRequestUrl,
|
||||||
conReq();
|
username,
|
||||||
});
|
password,
|
||||||
[username, password] = config.auth.connectionRequest(deviceId, connectionRequestUrl, username, password);
|
(u, p) => {
|
||||||
|
username = u;
|
||||||
|
password = p;
|
||||||
|
conReq();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
[username, password] = config.auth.connectionRequest(
|
||||||
|
deviceId,
|
||||||
|
connectionRequestUrl,
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
);
|
||||||
}
|
}
|
||||||
conReq();
|
conReq();
|
||||||
});
|
});
|
||||||
@@ -215,23 +279,18 @@ function connectionRequest(deviceId, callback) {
|
|||||||
|
|
||||||
function watchTask(deviceId, taskId, timeout, callback) {
|
function watchTask(deviceId, taskId, timeout, callback) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
db.tasksCollection.findOne({_id: taskId}, {_id: 1}, (err, task) => {
|
db.tasksCollection.findOne({ _id: taskId }, { _id: 1 }, (err, task) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (!task)
|
if (!task) return callback(null, "completed");
|
||||||
return callback(null, "completed");
|
|
||||||
|
|
||||||
const q = {_id: `${deviceId}:task_${taskId}`};
|
const q = { _id: `${deviceId}:task_${taskId}` };
|
||||||
db.faultsCollection.findOne(q, {_id: 1}, (err, fault) => {
|
db.faultsCollection.findOne(q, { _id: 1 }, (err, fault) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (fault)
|
if (fault) return callback(null, "fault");
|
||||||
return callback(null, "fault");
|
|
||||||
|
|
||||||
if ((timeout -= 500) <= 0)
|
if ((timeout -= 500) <= 0) return callback(null, "timeout");
|
||||||
return callback(null, "timeout");
|
|
||||||
|
|
||||||
watchTask(deviceId, taskId, timeout, callback);
|
watchTask(deviceId, taskId, timeout, callback);
|
||||||
});
|
});
|
||||||
@@ -244,53 +303,57 @@ function sanitizeTask(task, callback) {
|
|||||||
if (task.expiry) {
|
if (task.expiry) {
|
||||||
if (common.typeOf(task.expiry) === common.DATE_TYPE || isNaN(task.expiry))
|
if (common.typeOf(task.expiry) === common.DATE_TYPE || isNaN(task.expiry))
|
||||||
task.expiry = new Date(task.expiry);
|
task.expiry = new Date(task.expiry);
|
||||||
else
|
else task.expiry = new Date(task.timestamp.getTime() + +task.expiry * 1000);
|
||||||
task.expiry = new Date(task.timestamp.getTime() + +task.expiry * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(task);
|
callback(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertTasks(tasks, callback) {
|
function insertTasks(tasks, callback) {
|
||||||
if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE)
|
if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks];
|
||||||
tasks = [tasks];
|
else if (!tasks || tasks.length === 0) return callback(null, tasks || []);
|
||||||
else if (!tasks || tasks.length === 0)
|
|
||||||
return callback(null, tasks || []);
|
|
||||||
|
|
||||||
let counter = tasks.length;
|
let counter = tasks.length;
|
||||||
for (let task of tasks)
|
for (let task of tasks) {
|
||||||
sanitizeTask(task, (t) => {
|
sanitizeTask(task, t => {
|
||||||
if (t.uniqueKey)
|
if (t.uniqueKey) {
|
||||||
db.tasksCollection.remove({device: t.device, uniqueKey: t.uniqueKey}, () => {});
|
db.tasksCollection.remove(
|
||||||
|
{ device: t.device, uniqueKey: t.uniqueKey },
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (--counter === 0)
|
if (--counter === 0) db.tasksCollection.insert(tasks, callback);
|
||||||
db.tasksCollection.insert(tasks, callback);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDevice(deviceId, callback) {
|
function deleteDevice(deviceId, callback) {
|
||||||
db.tasksCollection.remove({"device": deviceId}, (err) => {
|
db.tasksCollection.remove({ device: deviceId }, err => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
db.devicesCollection.remove({ _id: deviceId }, err => {
|
||||||
db.devicesCollection.remove({"_id": deviceId}, (err) => {
|
if (err) return callback(err);
|
||||||
if (err)
|
db.faultsCollection.remove(
|
||||||
return callback(err);
|
{
|
||||||
db.faultsCollection.remove({
|
_id: {
|
||||||
"_id": {
|
$regex: `^${common.escapeRegExp(deviceId)}\\:`
|
||||||
"$regex": `^${common.escapeRegExp(deviceId)}\\:`
|
|
||||||
}
|
|
||||||
}, (err) => {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
db.operationsCollection.remove({
|
|
||||||
"_id": {
|
|
||||||
"$regex": `^${common.escapeRegExp(deviceId)}\\:`
|
|
||||||
}
|
}
|
||||||
}, (err) => {
|
},
|
||||||
if (err)
|
err => {
|
||||||
return callback(err);
|
if (err) return callback(err);
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, callback);
|
db.operationsCollection.remove(
|
||||||
});
|
{
|
||||||
});
|
_id: {
|
||||||
|
$regex: `^${common.escapeRegExp(deviceId)}\\:`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
cache.del(`${deviceId}_tasks_faults_operations`, callback);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-22
@@ -23,7 +23,7 @@ const crypto = require("crypto");
|
|||||||
function parseAuthHeader(authHeader) {
|
function parseAuthHeader(authHeader) {
|
||||||
authHeader = authHeader.trim();
|
authHeader = authHeader.trim();
|
||||||
const method = authHeader.split(" ", 1)[0];
|
const method = authHeader.split(" ", 1)[0];
|
||||||
const res = {method: method};
|
const res = { method: method };
|
||||||
const parts = authHeader.slice(method.length + 1).split(",");
|
const parts = authHeader.slice(method.length + 1).split(",");
|
||||||
|
|
||||||
let part;
|
let part;
|
||||||
@@ -38,8 +38,7 @@ function parseAuthHeader(authHeader) {
|
|||||||
} else {
|
} else {
|
||||||
while (!/[^\\]"\s*$/.test(value)) {
|
while (!/[^\\]"\s*$/.test(value)) {
|
||||||
const p = parts.shift();
|
const p = parts.shift();
|
||||||
if (p == null)
|
if (p == null) throw new Error("Unable to parse auth header");
|
||||||
throw new Error("Unable to parse auth header");
|
|
||||||
value += "," + p;
|
value += "," + p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ function parseAuthHeader(authHeader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function basic(username, password) {
|
function basic(username, password) {
|
||||||
return "Basic " + (new Buffer(`${username}:${password}`).toString("base64"));
|
return "Basic " + new Buffer(`${username}:${password}`).toString("base64");
|
||||||
}
|
}
|
||||||
|
|
||||||
function digest(username, password, uri, httpMethod, body, authHeader) {
|
function digest(username, password, uri, httpMethod, body, authHeader) {
|
||||||
@@ -64,40 +63,53 @@ function digest(username, password, uri, httpMethod, body, authHeader) {
|
|||||||
let qop;
|
let qop;
|
||||||
|
|
||||||
if (authHeader.qop) {
|
if (authHeader.qop) {
|
||||||
if (authHeader.qop.indexOf(",") !== -1)
|
if (authHeader.qop.indexOf(",") !== -1) qop = "auth";
|
||||||
qop = "auth"; // Either auth or auth-int, prefer auth
|
// Either auth or auth-int, prefer auth
|
||||||
else
|
else qop = authHeader.qop;
|
||||||
qop = authHeader.qop;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ha1 = crypto.createHash("md5");
|
let ha1 = crypto.createHash("md5");
|
||||||
ha1.update(username).update(":").update(authHeader.realm).update(":").update(password);
|
ha1
|
||||||
|
.update(username)
|
||||||
|
.update(":")
|
||||||
|
.update(authHeader.realm)
|
||||||
|
.update(":")
|
||||||
|
.update(password);
|
||||||
// TODO support "MD5-sess" algorithm directive
|
// TODO support "MD5-sess" algorithm directive
|
||||||
ha1 = ha1.digest("hex");
|
ha1 = ha1.digest("hex");
|
||||||
|
|
||||||
let ha2 = crypto.createHash("md5");
|
let ha2 = crypto.createHash("md5");
|
||||||
ha2.update(httpMethod).update(":").update(uri);
|
ha2
|
||||||
if (qop === "auth-int")
|
.update(httpMethod)
|
||||||
ha2.update(":").update(body);
|
.update(":")
|
||||||
|
.update(uri);
|
||||||
|
if (qop === "auth-int") ha2.update(":").update(body);
|
||||||
ha2 = ha2.digest("hex");
|
ha2 = ha2.digest("hex");
|
||||||
|
|
||||||
let hash = crypto.createHash("md5");
|
let hash = crypto.createHash("md5");
|
||||||
hash.update(ha1).update(":").update(authHeader.nonce);
|
hash
|
||||||
if (qop)
|
.update(ha1)
|
||||||
hash.update(":").update(nc).update(":").update(cnonce).update(":").update(qop);
|
.update(":")
|
||||||
|
.update(authHeader.nonce);
|
||||||
|
if (qop) {
|
||||||
|
hash
|
||||||
|
.update(":")
|
||||||
|
.update(nc)
|
||||||
|
.update(":")
|
||||||
|
.update(cnonce)
|
||||||
|
.update(":")
|
||||||
|
.update(qop);
|
||||||
|
}
|
||||||
hash.update(":").update(ha2);
|
hash.update(":").update(ha2);
|
||||||
|
|
||||||
let authString = `Digest username="${username}"`;
|
let authString = `Digest username="${username}"`;
|
||||||
authString += `,realm="${authHeader.realm}"`;
|
authString += `,realm="${authHeader.realm}"`;
|
||||||
authString += `,nonce="${authHeader.nonce}"`;
|
authString += `,nonce="${authHeader.nonce}"`;
|
||||||
authString += `,uri="${url}"`;
|
authString += `,uri="${uri}"`;
|
||||||
if (authHeader.algorithm)
|
if (authHeader.algorithm) authString += `,algorithm=${authHeader.algorithm}`;
|
||||||
authString += `,algorithm=${authHeader.algorithm}`;
|
if (qop) authString += `,qop=${qop},nc=${nc},cnonce="${cnonce}"`;
|
||||||
if (qop)
|
|
||||||
authString += `,qop=${qop},nc=${nc},cnonce="${cnonce}"`;
|
|
||||||
authString += `,response="${hash.digest("hex")}"`;
|
authString += `,response="${hash.digest("hex")}"`;
|
||||||
if (authHeader.opaque)
|
if (authHeader.opaque) authString += `,opaque="${authHeader.opaque}"`;
|
||||||
authString += `,opaque="${authHeader.opaque}"`;
|
|
||||||
|
|
||||||
return authString;
|
return authString;
|
||||||
}
|
}
|
||||||
|
|||||||
+104
-110
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -27,7 +27,6 @@ let mongoDb;
|
|||||||
let mongoCollection;
|
let mongoCollection;
|
||||||
let mongoTimeOffset = 0;
|
let mongoTimeOffset = 0;
|
||||||
|
|
||||||
|
|
||||||
function connect(callback) {
|
function connect(callback) {
|
||||||
const REDIS_HOST = config.get("REDIS_HOST");
|
const REDIS_HOST = config.get("REDIS_HOST");
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@ function connect(callback) {
|
|||||||
const REDIS_PORT = config.get("REDIS_PORT");
|
const REDIS_PORT = config.get("REDIS_PORT");
|
||||||
const REDIS_DB = config.get("REDIS_DB");
|
const REDIS_DB = config.get("REDIS_DB");
|
||||||
redisClient = redis.createClient(REDIS_PORT, REDIS_HOST);
|
redisClient = redis.createClient(REDIS_PORT, REDIS_HOST);
|
||||||
redisClient.select(REDIS_DB, function(err) {
|
redisClient.select(REDIS_DB, err => {
|
||||||
exports.get = redisGet;
|
exports.get = redisGet;
|
||||||
exports.set = redisSet;
|
exports.set = redisSet;
|
||||||
exports.del = redisDel;
|
exports.del = redisDel;
|
||||||
@@ -44,191 +43,184 @@ function connect(callback) {
|
|||||||
exports.lock = redisLock;
|
exports.lock = redisLock;
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const mongodb = require("mongodb");
|
const mongodb = require("mongodb");
|
||||||
const MONGODB_CONNECTION_URL = config.get("MONGODB_CONNECTION_URL");
|
const MONGODB_CONNECTION_URL = config.get("MONGODB_CONNECTION_URL");
|
||||||
|
|
||||||
mongodb.MongoClient.connect(MONGODB_CONNECTION_URL, function(err, db) {
|
mongodb.MongoClient.connect(
|
||||||
if (err)
|
MONGODB_CONNECTION_URL,
|
||||||
return callback(err);
|
(err, db) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
mongoDb = db;
|
mongoDb = db;
|
||||||
mongoCollection = db.collection("cache");
|
mongoCollection = db.collection("cache");
|
||||||
mongoCollection.ensureIndex({"expire": 1}, {expireAfterSeconds: 0});
|
mongoCollection.ensureIndex({ expire: 1 }, { expireAfterSeconds: 0 });
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
mongoDb.command({hostInfo: 1}, function(err, res) {
|
mongoDb.command({ hostInfo: 1 }, (err, res) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
mongoTimeOffset = res.system.currentTime.getTime() - now;
|
||||||
mongoTimeOffset = res.system.currentTime.getTime() - now;
|
exports.get = mongoGet;
|
||||||
exports.get = mongoGet;
|
exports.set = mongoSet;
|
||||||
exports.set = mongoSet;
|
exports.del = mongoDel;
|
||||||
exports.del = mongoDel;
|
exports.pop = mongoPop;
|
||||||
exports.pop = mongoPop;
|
exports.lock = mongoLock;
|
||||||
exports.lock = mongoLock;
|
callback();
|
||||||
callback();
|
});
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
if (redisClient)
|
if (redisClient) redisClient.quit();
|
||||||
redisClient.quit();
|
|
||||||
|
|
||||||
if (mongoDb)
|
if (mongoDb) mongoDb.close();
|
||||||
mongoDb.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function redisGet(key, callback) {
|
function redisGet(key, callback) {
|
||||||
if (Array.isArray(key))
|
if (Array.isArray(key)) redisClient.mget(key, callback);
|
||||||
redisClient.mget(key, callback);
|
else redisClient.get(key, callback);
|
||||||
else
|
|
||||||
redisClient.get(key, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function redisDel(key, callback) {
|
function redisDel(key, callback) {
|
||||||
redisClient.del(key, callback);
|
redisClient.del(key, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function redisSet(key, value, ttl, callback) {
|
function redisSet(key, value, ttl, callback) {
|
||||||
if (!callback && typeof v === "function") {
|
if (!callback && typeof v === "function") {
|
||||||
callback = ttl;
|
callback = ttl;
|
||||||
ttl = null;
|
ttl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ttl)
|
if (!ttl) ttl = MAX_CACHE_TTL;
|
||||||
ttl = MAX_CACHE_TTL;
|
|
||||||
|
|
||||||
redisClient.setex(key, ttl, value, callback);
|
redisClient.setex(key, ttl, value, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function redisPop(key, callback) {
|
function redisPop(key, callback) {
|
||||||
const POP_SCRIPT = "local v=redis.call(\"get\",KEYS[1]);redis.call(\"del\",KEYS[1]);return v;";
|
const POP_SCRIPT =
|
||||||
|
'local v=redis.call("get",KEYS[1]);redis.call("del",KEYS[1]);return v;';
|
||||||
redisClient.eval(POP_SCRIPT, 1, key, callback);
|
redisClient.eval(POP_SCRIPT, 1, key, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function redisLock(lockName, ttl, callback) {
|
function redisLock(lockName, ttl, callback) {
|
||||||
const UNLOCK_SCRIPT = "if redis.call(\"get\", KEYS[1]) == ARGV[1] then return redis.call(\"del\", KEYS[1]) else return 0 end";
|
const UNLOCK_SCRIPT =
|
||||||
const EXTEND_SCRIPT = "if redis.call(\"get\", KEYS[1]) == ARGV[1] then return redis.call(\"pexpire\", KEYS[1], ARGV[2]) else return 0 end";
|
'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end';
|
||||||
const token = Math.random().toString(36).slice(2);
|
const EXTEND_SCRIPT =
|
||||||
|
'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end';
|
||||||
|
const token = Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.slice(2);
|
||||||
|
|
||||||
function unlockOrExtend(ttl) {
|
function unlockOrExtend(extendTtl) {
|
||||||
if (!ttl) {
|
if (!extendTtl) {
|
||||||
redisClient.eval(UNLOCK_SCRIPT, 1, lockName, token, function(err, res) {
|
redisClient.eval(UNLOCK_SCRIPT, 1, lockName, token, (err, res) => {
|
||||||
if (err || !res)
|
if (err || !res) throw err || new Error("Lock expired");
|
||||||
throw err || new Error("Lock expired");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
redisClient.eval(EXTEND_SCRIPT, 1, lockName, token, ttl, function(err, res) {
|
|
||||||
if (err || !res)
|
|
||||||
throw err || new Error("Lock expired");
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
redisClient.eval(
|
||||||
|
EXTEND_SCRIPT,
|
||||||
|
1,
|
||||||
|
lockName,
|
||||||
|
token,
|
||||||
|
extendTtl,
|
||||||
|
(err, res) => {
|
||||||
|
if (err || !res) throw err || new Error("Lock expired");
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redisClient.set(lockName, token, "NX", "EX", ttl, function(err, res) {
|
redisClient.set(lockName, token, "NX", "EX", ttl, (err, res) => {
|
||||||
if (err || res)
|
if (err || res) return callback(err, unlockOrExtend);
|
||||||
return callback(err, unlockOrExtend);
|
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(() => {
|
||||||
redisLock(lockName, ttl, callback);
|
redisLock(lockName, ttl, callback);
|
||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mongoGet(key, callback) {
|
function mongoGet(key, callback) {
|
||||||
let expire = new Date(Date.now() - mongoTimeOffset);
|
let expire = new Date(Date.now() - mongoTimeOffset);
|
||||||
if (Array.isArray(key))
|
if (Array.isArray(key)) {
|
||||||
mongoCollection.find({"_id": {"$in": key}}).toArray(function(err, res) {
|
mongoCollection.find({ _id: { $in: key } }).toArray((err, res) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
let indices = {};
|
let indices = {};
|
||||||
key.forEach(function(v, i) {
|
key.forEach((v, i) => {
|
||||||
indices[v] = i;
|
indices[v] = i;
|
||||||
});
|
});
|
||||||
|
|
||||||
let values = [];
|
let values = [];
|
||||||
res.forEach(function(r) {
|
res.forEach(r => {
|
||||||
if (r["expire"] > expire)
|
if (r["expire"] > expire) values[indices[r["_id"]]] = r["value"];
|
||||||
values[indices[r["_id"]]] = r["value"];
|
|
||||||
});
|
});
|
||||||
callback(null, values);
|
callback(null, values);
|
||||||
});
|
});
|
||||||
else
|
} else {
|
||||||
mongoCollection.findOne({"_id": {"$in": [key]}}, function(err, res) {
|
mongoCollection.findOne({ _id: { $in: [key] } }, (err, res) => {
|
||||||
if (err || !res)
|
if (err || !res) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (res["expire"] > expire)
|
if (res["expire"] > expire) return callback(null, res["value"]);
|
||||||
return callback(null, res["value"]);
|
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mongoDel(key, callback) {
|
function mongoDel(key, callback) {
|
||||||
if (Array.isArray(key))
|
if (Array.isArray(key))
|
||||||
mongoCollection.remove({"_id": {"$in": key}}, callback);
|
mongoCollection.remove({ _id: { $in: key } }, callback);
|
||||||
else
|
else mongoCollection.remove({ _id: key }, callback);
|
||||||
mongoCollection.remove({"_id": key}, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mongoSet(key, value, ttl, callback) {
|
function mongoSet(key, value, ttl, callback) {
|
||||||
if (!callback && typeof v === "function") {
|
if (!callback && typeof v === "function") {
|
||||||
callback = ttl;
|
callback = ttl;
|
||||||
ttl = null;
|
ttl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ttl)
|
if (!ttl) ttl = MAX_CACHE_TTL;
|
||||||
ttl = MAX_CACHE_TTL;
|
|
||||||
|
|
||||||
let expire = new Date((Date.now() - mongoTimeOffset) + (ttl * 1000));
|
let expire = new Date(Date.now() - mongoTimeOffset + ttl * 1000);
|
||||||
mongoCollection.save({"_id": key, "value": value, "expire": expire}, callback);
|
mongoCollection.save({ _id: key, value: value, expire: expire }, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mongoPop(key, callback) {
|
function mongoPop(key, callback) {
|
||||||
mongoCollection.findAndModify({"_id": key}, null, null, {remove: true}, function(err, res) {
|
mongoCollection.findAndModify(
|
||||||
if (err || !res["value"])
|
{ _id: key },
|
||||||
return callback(err);
|
null,
|
||||||
|
null,
|
||||||
|
{ remove: true },
|
||||||
|
(err, res) => {
|
||||||
|
if (err || !res["value"]) return callback(err);
|
||||||
|
|
||||||
if (res["value"]["expire"] > new Date(Date.now() - mongoTimeOffset))
|
if (res["value"]["expire"] > new Date(Date.now() - mongoTimeOffset))
|
||||||
return callback(null, res["value"]["value"]);
|
return callback(null, res["value"]["value"]);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mongoLock(lockName, ttl, callback) {
|
function mongoLock(lockName, ttl, callback) {
|
||||||
const token = Math.random().toString(36).slice(2);
|
const token = Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.slice(2);
|
||||||
|
|
||||||
function unlockOrExtend(ttl) {
|
function unlockOrExtend(extendTtl) {
|
||||||
if (!ttl) {
|
if (!extendTtl) {
|
||||||
mongoCollection.remove({"_id": lockName, "value": token}, function(err, res) {
|
mongoCollection.remove({ _id: lockName, value: token }, (err, res) => {
|
||||||
if (err || res["result"]["n"] !== 1)
|
if (err || res["result"]["n"] !== 1)
|
||||||
throw err || new Error("Lock expired");
|
throw err || new Error("Lock expired");
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
let expire = new Date(Date.now() - mongoTimeOffset + extendTtl * 1000);
|
||||||
let expire = new Date((Date.now() - mongoTimeOffset) + (ttl * 1000));
|
|
||||||
mongoCollection.update(
|
mongoCollection.update(
|
||||||
{"_id": lockName, "value": token},
|
{ _id: lockName, value: token },
|
||||||
{"expire": expire},
|
{ expire: expire },
|
||||||
function(err, res) {
|
(err, res) => {
|
||||||
if (err || res["result"]["n"] !== 1)
|
if (err || res["result"]["n"] !== 1)
|
||||||
throw err | new Error("Lock expired");
|
throw err | new Error("Lock expired");
|
||||||
}
|
}
|
||||||
@@ -237,21 +229,23 @@ function mongoLock(lockName, ttl, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let expireTest = new Date(Date.now() - mongoTimeOffset);
|
let expireTest = new Date(Date.now() - mongoTimeOffset);
|
||||||
let expireSet = new Date((Date.now() - mongoTimeOffset) + (ttl * 1000));
|
let expireSet = new Date(Date.now() - mongoTimeOffset + ttl * 1000);
|
||||||
|
|
||||||
mongoCollection.update(
|
mongoCollection.update(
|
||||||
{"_id": lockName, "expire": {"$lte": expireTest}},
|
{ _id: lockName, expire: { $lte: expireTest } },
|
||||||
{"$set": {"value" : token, "expire": expireSet}},
|
{ $set: { value: token, expire: expireSet } },
|
||||||
{upsert: true},
|
{ upsert: true },
|
||||||
function(err, res) {
|
err => {
|
||||||
if (err && err.code === 11000)
|
if (err && err.code === 11000) {
|
||||||
return setTimeout(function() {mongoLock(lockName, ttl, callback)}, 200);
|
return setTimeout(() => {
|
||||||
|
mongoLock(lockName, ttl, callback);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
return callback(err, unlockOrExtend);
|
return callback(err, unlockOrExtend);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.connect = connect;
|
exports.connect = connect;
|
||||||
exports.disconnect = disconnect;
|
exports.disconnect = disconnect;
|
||||||
+30
-36
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -30,43 +30,43 @@ let tooManyCrashesTimestamp = 0;
|
|||||||
function getDependencyVersions() {
|
function getDependencyVersions() {
|
||||||
let dependencies = [`node@${process.versions.node}`];
|
let dependencies = [`node@${process.versions.node}`];
|
||||||
try {
|
try {
|
||||||
let npmls = JSON.parse(require("child_process").execSync("npm ls --prod --json --depth 0"));
|
let npmls = JSON.parse(
|
||||||
|
require("child_process").execSync("npm ls --prod --json --depth 0")
|
||||||
|
);
|
||||||
|
|
||||||
for (let k in npmls.dependencies)
|
for (let k in npmls.dependencies)
|
||||||
dependencies.push(`${k}@${npmls.dependencies[k].version}`);
|
dependencies.push(`${k}@${npmls.dependencies[k].version}`);
|
||||||
} catch (err) {}
|
} catch (err) {
|
||||||
|
// Continue regardless of error
|
||||||
|
}
|
||||||
|
|
||||||
return dependencies.join(",");
|
return dependencies.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getConfig() {
|
function getConfig() {
|
||||||
let conf = [];
|
let conf = [];
|
||||||
const keys = Object.keys(process.env);
|
const keys = Object.keys(process.env);
|
||||||
for (let k of keys)
|
for (let k of keys) {
|
||||||
if (k.startsWith("GENIEACS_")) {
|
if (k.startsWith("GENIEACS_")) {
|
||||||
const name = k.slice(9);
|
const name = k.slice(9);
|
||||||
const value = process.env[k];
|
const value = process.env[k];
|
||||||
const def = config.getDefault(name);
|
const def = config.getDefault(name);
|
||||||
if (def == null || value !== `${def}`)
|
if (def == null || value !== `${def}`) conf.push(`${name}=${value}`);
|
||||||
conf.push(`${name}=${value}`);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return conf.join(",");
|
return conf.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function restartWorker(worker, code, signal) {
|
function restartWorker(worker, code, signal) {
|
||||||
let msg = {
|
let msg = {
|
||||||
message: "Worker died",
|
message: "Worker died",
|
||||||
pid: worker.process.pid
|
pid: worker.process.pid
|
||||||
};
|
};
|
||||||
|
|
||||||
if (code != null)
|
if (code != null) msg.exitCode = code;
|
||||||
msg.exitCode = code;
|
|
||||||
|
|
||||||
if (signal != null)
|
if (signal != null) msg.signal = signal;
|
||||||
msg.signal = signal;
|
|
||||||
|
|
||||||
logger.error(msg);
|
logger.error(msg);
|
||||||
|
|
||||||
@@ -75,14 +75,13 @@ function restartWorker(worker, code, signal) {
|
|||||||
if (respawnTimestamp === now) {
|
if (respawnTimestamp === now) {
|
||||||
tooManyCrashesTimestamp = now;
|
tooManyCrashesTimestamp = now;
|
||||||
cluster.fork();
|
cluster.fork();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now - tooManyCrashesTimestamp > 60000) {
|
if (now - tooManyCrashesTimestamp > 60000) {
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
cluster.removeListener("exit", restartWorker);
|
cluster.removeListener("exit", restartWorker);
|
||||||
for (let pid in cluster.workers)
|
for (let pid in cluster.workers) cluster.workers[pid].kill();
|
||||||
cluster.workers[pid].kill();
|
|
||||||
|
|
||||||
logger.error({
|
logger.error({
|
||||||
message: "Too many crashes, exiting",
|
message: "Too many crashes, exiting",
|
||||||
@@ -91,18 +90,16 @@ function restartWorker(worker, code, signal) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(() => {
|
||||||
if (process.exitCode) return;
|
if (process.exitCode) return;
|
||||||
cluster.fork();
|
cluster.fork();
|
||||||
}, respawnTimestamp - now);
|
}, respawnTimestamp - now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function start(service) {
|
function start(service) {
|
||||||
logger.init(service, VERSION);
|
logger.init(service, VERSION);
|
||||||
const config = require("./config");
|
const SERVICE_ADDRESS = config.get(`${service.toUpperCase()}_INTERFACE`);
|
||||||
const SERVICE_ADDRESS = config.get(`${service.toUpperCase()}_INTERFACE`)
|
const SERVICE_PORT = config.get(`${service.toUpperCase()}_PORT`);
|
||||||
const SERVICE_PORT = config.get(`${service.toUpperCase()}_PORT`)
|
|
||||||
|
|
||||||
logger.info({
|
logger.info({
|
||||||
message: `GenieACS (genieacs-${service}) starting`,
|
message: `GenieACS (genieacs-${service}) starting`,
|
||||||
@@ -112,9 +109,12 @@ function start(service) {
|
|||||||
config: getConfig()
|
config: getConfig()
|
||||||
});
|
});
|
||||||
|
|
||||||
cluster.on("listening", function(worker, address) {
|
cluster.on("listening", (worker, address) => {
|
||||||
if ((address.addressType === 4 || address.addressType === 6) &&
|
if (
|
||||||
address.address === SERVICE_ADDRESS && address.port === SERVICE_PORT) {
|
(address.addressType === 4 || address.addressType === 6) &&
|
||||||
|
address.address === SERVICE_ADDRESS &&
|
||||||
|
address.port === SERVICE_PORT
|
||||||
|
) {
|
||||||
logger.info({
|
logger.info({
|
||||||
message: "Worker listening",
|
message: "Worker listening",
|
||||||
pid: worker.process.pid,
|
pid: worker.process.pid,
|
||||||
@@ -126,7 +126,7 @@ function start(service) {
|
|||||||
|
|
||||||
cluster.on("exit", restartWorker);
|
cluster.on("exit", restartWorker);
|
||||||
|
|
||||||
process.on("SIGINT", function() {
|
process.on("SIGINT", () => {
|
||||||
logger.info({
|
logger.info({
|
||||||
message: "Received signal SIGINT, exiting",
|
message: "Received signal SIGINT, exiting",
|
||||||
pid: process.pid
|
pid: process.pid
|
||||||
@@ -135,15 +135,14 @@ function start(service) {
|
|||||||
cluster.removeListener("exit", restartWorker);
|
cluster.removeListener("exit", restartWorker);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", function() {
|
process.on("SIGTERM", () => {
|
||||||
logger.info({
|
logger.info({
|
||||||
message: "Received signal SIGTERM, exiting",
|
message: "Received signal SIGTERM, exiting",
|
||||||
pid: process.pid
|
pid: process.pid
|
||||||
});
|
});
|
||||||
|
|
||||||
cluster.removeListener("exit", restartWorker);
|
cluster.removeListener("exit", restartWorker);
|
||||||
for (let pid in cluster.workers)
|
for (let pid in cluster.workers) cluster.workers[pid].kill();
|
||||||
cluster.workers[pid].kill();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cluster.setupMaster({
|
cluster.setupMaster({
|
||||||
@@ -153,15 +152,12 @@ function start(service) {
|
|||||||
|
|
||||||
let workerCount = config.get(`${service.toUpperCase()}_WORKER_PROCESSES`);
|
let workerCount = config.get(`${service.toUpperCase()}_WORKER_PROCESSES`);
|
||||||
|
|
||||||
if (!workerCount)
|
if (!workerCount) workerCount = Math.max(2, require("os").cpus().length);
|
||||||
workerCount = Math.max(2, require("os").cpus().length);
|
|
||||||
|
|
||||||
for (let i = 0; i < workerCount; ++ i)
|
for (let i = 0; i < workerCount; ++i) cluster.fork();
|
||||||
cluster.fork();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process.on("uncaughtException", err => {
|
||||||
process.on("uncaughtException", function(err) {
|
|
||||||
logger.error({
|
logger.error({
|
||||||
message: "Uncaught exception in master process, exiting",
|
message: "Uncaught exception in master process, exiting",
|
||||||
exception: err,
|
exception: err,
|
||||||
@@ -169,9 +165,7 @@ process.on("uncaughtException", function(err) {
|
|||||||
});
|
});
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
cluster.removeListener("exit", restartWorker);
|
cluster.removeListener("exit", restartWorker);
|
||||||
for (let pid in cluster.workers)
|
for (let pid in cluster.workers) cluster.workers[pid].kill();
|
||||||
cluster.workers[pid].kill();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
exports.start = start;
|
exports.start = start;
|
||||||
+40
-53
@@ -28,24 +28,30 @@ const OBJECT_TYPE = "[object Object]";
|
|||||||
const REGEXP_TYPE = "[object RegExp]";
|
const REGEXP_TYPE = "[object RegExp]";
|
||||||
const DATE_TYPE = "[object Date]";
|
const DATE_TYPE = "[object Date]";
|
||||||
|
|
||||||
const typeOf = (obj) => Object.prototype.toString.call(obj);
|
const typeOf = obj => Object.prototype.toString.call(obj);
|
||||||
|
|
||||||
function generateDeviceId(deviceIdStruct) {
|
function generateDeviceId(deviceIdStruct) {
|
||||||
// Escapes everything except alphanumerics and underscore
|
// Escapes everything except alphanumerics and underscore
|
||||||
function esc(str) {
|
function esc(str) {
|
||||||
return str.replace(/[^A-Za-z0-9_]/g, (chr) => {
|
return str.replace(/[^A-Za-z0-9_]/g, chr => {
|
||||||
const buf = new Buffer(chr, "utf8");
|
const buf = new Buffer(chr, "utf8");
|
||||||
let rep = "";
|
let rep = "";
|
||||||
for (let b of buf)
|
for (let b of buf) rep += "%" + b.toString(16).toUpperCase();
|
||||||
rep += "%" + (b.toString(16).toUpperCase());
|
|
||||||
return rep;
|
return rep;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guaranteeing globally unique id as defined in TR-069
|
// Guaranteeing globally unique id as defined in TR-069
|
||||||
if (deviceIdStruct["ProductClass"])
|
if (deviceIdStruct["ProductClass"]) {
|
||||||
return (esc(deviceIdStruct["OUI"])) + "-" + (esc(deviceIdStruct["ProductClass"])) + "-" + (esc(deviceIdStruct["SerialNumber"]));
|
return (
|
||||||
return (esc(deviceIdStruct["OUI"])) + "-" + (esc(deviceIdStruct["SerialNumber"]));
|
esc(deviceIdStruct["OUI"]) +
|
||||||
|
"-" +
|
||||||
|
esc(deviceIdStruct["ProductClass"]) +
|
||||||
|
"-" +
|
||||||
|
esc(deviceIdStruct["SerialNumber"])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return esc(deviceIdStruct["OUI"]) + "-" + esc(deviceIdStruct["SerialNumber"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAlias(pattern, start, res) {
|
function parseAlias(pattern, start, res) {
|
||||||
@@ -53,22 +59,21 @@ function parseAlias(pattern, start, res) {
|
|||||||
let i = start;
|
let i = start;
|
||||||
while (i < pattern.length && pattern[i] !== "]") {
|
while (i < pattern.length && pattern[i] !== "]") {
|
||||||
const alias = [];
|
const alias = [];
|
||||||
let j = i = parsePath(pattern, i, alias) + 1;
|
let j = (i = parsePath(pattern, i, alias) + 1);
|
||||||
while (pattern[j] !== "]" && pattern[j] !== ",") {
|
while (pattern[j] !== "]" && pattern[j] !== ",") {
|
||||||
if (pattern[j] === "\"" && i === j) {
|
if (pattern[j] === '"' && i === j) {
|
||||||
++j;
|
++j;
|
||||||
while (pattern[j] !== "\"" || pattern[j - 1] === "\\") {
|
while (pattern[j] !== '"' || pattern[j - 1] === "\\") {
|
||||||
if (++j >= pattern.length)
|
if (++j >= pattern.length)
|
||||||
throw new Error("Invalid alias expression");
|
throw new Error("Invalid alias expression");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (++j >= pattern.length)
|
if (++j >= pattern.length) throw new Error("Invalid alias expression");
|
||||||
throw new Error("Invalid alias expression");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = pattern.slice(i, j).trim();
|
let value = pattern.slice(i, j).trim();
|
||||||
i = j;
|
i = j;
|
||||||
if (value[0] === "\"") {
|
if (value[0] === '"') {
|
||||||
try {
|
try {
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -78,8 +83,7 @@ function parseAlias(pattern, start, res) {
|
|||||||
|
|
||||||
alias.push(value);
|
alias.push(value);
|
||||||
aliases.push(alias);
|
aliases.push(alias);
|
||||||
if (pattern[i] === ",")
|
if (pattern[i] === ",") ++i;
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort to ensure identical expressions have idential string representation
|
// Sort to ensure identical expressions have idential string representation
|
||||||
@@ -87,20 +91,15 @@ function parseAlias(pattern, start, res) {
|
|||||||
const jMax = Math.min(a.length, b.length);
|
const jMax = Math.min(a.length, b.length);
|
||||||
for (let j = 0; j < jMax; j += 2) {
|
for (let j = 0; j < jMax; j += 2) {
|
||||||
const kMax = Math.min(a[j].length, b[j].length);
|
const kMax = Math.min(a[j].length, b[j].length);
|
||||||
for (let k = 0; k < kMax; ++ k) {
|
for (let k = 0; k < kMax; ++k) {
|
||||||
if (Array.isArray(a[j][k])) {
|
if (Array.isArray(a[j][k])) {
|
||||||
if (Array.isArray(b[j][k]))
|
if (Array.isArray(b[j][k])) return srt(a[j][k], b[j][k]);
|
||||||
return srt(a[j][k], b[j][k]);
|
else if (b[j][k] == null) return -1;
|
||||||
else if (b[j][k] == null)
|
else return 1;
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
} else if (a[j][k] == null) {
|
} else if (a[j][k] == null) {
|
||||||
if (b[j][k] == null)
|
if (b[j][k] == null) return 0;
|
||||||
return 0;
|
else return 1;
|
||||||
else
|
} else if (b[j][k] == null || Array.isArray(b[j][k])) {
|
||||||
return 1;
|
|
||||||
} else if ((b[j][k] == null) || Array.isArray(b[j][k])) {
|
|
||||||
return -1;
|
return -1;
|
||||||
} else if (a[j][k] > b[j][k]) {
|
} else if (a[j][k] > b[j][k]) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -109,21 +108,15 @@ function parseAlias(pattern, start, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a[j].length > b[j].length)
|
if (a[j].length > b[j].length) return -1;
|
||||||
return -1;
|
else if (a[j].length < b[j].length) return 1;
|
||||||
else if (a[j].length < b[j].length)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (a[j + 1] > b[j + 1])
|
if (a[j + 1] > b[j + 1]) return -1;
|
||||||
return -1;
|
else if (a[j + 1] < b[j + 1]) return 1;
|
||||||
else if (a[j + 1] < b[j + 1])
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.length > b.length)
|
if (a.length > b.length) return -1;
|
||||||
return -1;
|
else if (a.length < b.length) return 1;
|
||||||
else if (a.length < b.length)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -141,7 +134,7 @@ function parsePath(pattern, start, res) {
|
|||||||
|
|
||||||
// Colon separator is needed for parseAlias
|
// Colon separator is needed for parseAlias
|
||||||
if (i < pattern.length && pattern[i] !== ":") {
|
if (i < pattern.length && pattern[i] !== ":") {
|
||||||
while (true) {
|
for (;;) {
|
||||||
if (pattern[i] === "[") {
|
if (pattern[i] === "[") {
|
||||||
path.alias |= 1 << path.length;
|
path.alias |= 1 << path.length;
|
||||||
i = parseAlias(pattern, i + 1, path) + 1;
|
i = parseAlias(pattern, i + 1, path) + 1;
|
||||||
@@ -151,15 +144,12 @@ function parsePath(pattern, start, res) {
|
|||||||
++i;
|
++i;
|
||||||
|
|
||||||
const n = pattern.slice(j, i);
|
const n = pattern.slice(j, i);
|
||||||
if (n === "*")
|
if (n === "*") path.wildcard |= 1 << path.length;
|
||||||
path.wildcard |= 1 << path.length;
|
|
||||||
path.push(n);
|
path.push(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >= pattern.length || pattern[i] === ":")
|
if (i >= pattern.length || pattern[i] === ":") break;
|
||||||
break;
|
else if (pattern[i] !== ".") throw new Error("Invalid alias expression");
|
||||||
else if (pattern[i] !== ".")
|
|
||||||
throw new Error("Invalid alias expression");
|
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -167,16 +157,14 @@ function parsePath(pattern, start, res) {
|
|||||||
|
|
||||||
Object.freeze(path);
|
Object.freeze(path);
|
||||||
|
|
||||||
if (res == null)
|
if (res == null) return path;
|
||||||
return path;
|
|
||||||
|
|
||||||
res.push(path);
|
res.push(path);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPathMeta(path) {
|
function addPathMeta(path) {
|
||||||
if (path.alias != null && path.wildcard != null)
|
if (path.alias != null && path.wildcard != null) return path;
|
||||||
return path;
|
|
||||||
|
|
||||||
path.alias = 0;
|
path.alias = 0;
|
||||||
path.wildcard = 0;
|
path.wildcard = 0;
|
||||||
@@ -184,8 +172,7 @@ function addPathMeta(path) {
|
|||||||
for (let [i, p] of path.entries()) {
|
for (let [i, p] of path.entries()) {
|
||||||
if (Array.isArray(p)) {
|
if (Array.isArray(p)) {
|
||||||
path.alias |= 1 << i;
|
path.alias |= 1 << i;
|
||||||
for (let j = 0; j < p.length; j += 2)
|
for (let j = 0; j < p.length; j += 2) addPathMeta(p[j]);
|
||||||
addPathMeta(p[j]);
|
|
||||||
} else if (p === "*") {
|
} else if (p === "*") {
|
||||||
path.wildcard |= 1 << i;
|
path.wildcard |= 1 << i;
|
||||||
}
|
}
|
||||||
@@ -197,7 +184,7 @@ function addPathMeta(path) {
|
|||||||
|
|
||||||
// Source: http://stackoverflow.com/a/6969486
|
// Source: http://stackoverflow.com/a/6969486
|
||||||
function escapeRegExp(str) {
|
function escapeRegExp(str) {
|
||||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.UNDEFINED_TYPE = UNDEFINED_TYPE;
|
exports.UNDEFINED_TYPE = UNDEFINED_TYPE;
|
||||||
|
|||||||
+86
-85
@@ -21,81 +21,89 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
CONFIG_DIR: {type: "path", "default": "config"},
|
CONFIG_DIR: { type: "path", default: "config" },
|
||||||
MONGODB_CONNECTION_URL: {type: "string", "default": "mongodb://127.0.0.1/genieacs"},
|
MONGODB_CONNECTION_URL: {
|
||||||
REDIS_PORT: {type: "int", "default": 6379},
|
type: "string",
|
||||||
REDIS_HOST: {type: "string", "default": ""},
|
default: "mongodb://127.0.0.1/genieacs"
|
||||||
REDIS_DB: {type: "int", "default": 0},
|
},
|
||||||
|
REDIS_PORT: { type: "int", default: 6379 },
|
||||||
|
REDIS_HOST: { type: "string", default: "" },
|
||||||
|
REDIS_DB: { type: "int", default: 0 },
|
||||||
|
|
||||||
CWMP_WORKER_PROCESSES: {type: "int", "default": 0},
|
CWMP_WORKER_PROCESSES: { type: "int", default: 0 },
|
||||||
CWMP_PORT: {type: "int", "default": 7547},
|
CWMP_PORT: { type: "int", default: 7547 },
|
||||||
CWMP_INTERFACE: {type: "string", "default": "0.0.0.0"},
|
CWMP_INTERFACE: { type: "string", default: "0.0.0.0" },
|
||||||
CWMP_SSL: {type: "bool", "default": false},
|
CWMP_SSL: { type: "bool", default: false },
|
||||||
CWMP_LOG_FILE: {type: "path", "default": ""},
|
CWMP_LOG_FILE: { type: "path", default: "" },
|
||||||
CWMP_ACCESS_LOG_FILE: {type: "path", "default": ""},
|
CWMP_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||||
|
|
||||||
NBI_WORKER_PROCESSES: {type: "int", "default": 0},
|
NBI_WORKER_PROCESSES: { type: "int", default: 0 },
|
||||||
NBI_PORT: {type: "int", "default": 7557},
|
NBI_PORT: { type: "int", default: 7557 },
|
||||||
NBI_INTERFACE: {type: "string", "default": "0.0.0.0"},
|
NBI_INTERFACE: { type: "string", default: "0.0.0.0" },
|
||||||
NBI_SSL: {type: "bool", "default": false},
|
NBI_SSL: { type: "bool", default: false },
|
||||||
NBI_LOG_FILE: {type: "path", "default": ""},
|
NBI_LOG_FILE: { type: "path", default: "" },
|
||||||
NBI_ACCESS_LOG_FILE: {type: "path", "default": ""},
|
NBI_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||||
|
|
||||||
FS_WORKER_PROCESSES: {type: "int", "default": 0},
|
FS_WORKER_PROCESSES: { type: "int", default: 0 },
|
||||||
FS_PORT: {type: "int", "default": 7567},
|
FS_PORT: { type: "int", default: 7567 },
|
||||||
FS_INTERFACE: {type: "string", "default": "0.0.0.0"},
|
FS_INTERFACE: { type: "string", default: "0.0.0.0" },
|
||||||
FS_SSL: {type: "bool", "default": false},
|
FS_SSL: { type: "bool", default: false },
|
||||||
FS_HOSTNAME: {type: "string", "default": "acs.example.com"},
|
FS_HOSTNAME: { type: "string", default: "acs.example.com" },
|
||||||
FS_LOG_FILE: {type: "path", "default": ""},
|
FS_LOG_FILE: { type: "path", default: "" },
|
||||||
FS_ACCESS_LOG_FILE: {type: "path", "default": ""},
|
FS_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||||
|
|
||||||
UDP_CONNECTION_REQUEST_PORT: {type: "int", "default": 0},
|
UDP_CONNECTION_REQUEST_PORT: { type: "int", default: 0 },
|
||||||
|
|
||||||
DOWNLOAD_TIMEOUT: {type: "int", "default": 3600},
|
DOWNLOAD_TIMEOUT: { type: "int", default: 3600 },
|
||||||
EXT_TIMEOUT: {type: "int", "default": 3000},
|
EXT_TIMEOUT: { type: "int", default: 3000 },
|
||||||
MAX_CACHE_TTL: {type: "int", "default": 86400},
|
MAX_CACHE_TTL: { type: "int", default: 86400 },
|
||||||
DEBUG: {type: "bool", "default": false},
|
DEBUG: { type: "bool", default: false },
|
||||||
RETRY_DELAY: {type: "int", "default": 300},
|
RETRY_DELAY: { type: "int", default: 300 },
|
||||||
SESSION_TIMEOUT: {type: "int", "default": 30},
|
SESSION_TIMEOUT: { type: "int", default: 30 },
|
||||||
CONNECTION_REQUEST_TIMEOUT: {type: "int", "default": 2000},
|
CONNECTION_REQUEST_TIMEOUT: { type: "int", default: 2000 },
|
||||||
GPN_NEXT_LEVEL: {type: "int", "default": 0},
|
GPN_NEXT_LEVEL: { type: "int", default: 0 },
|
||||||
GPV_BATCH_SIZE: {type: "int", "default": 32},
|
GPV_BATCH_SIZE: { type: "int", default: 32 },
|
||||||
MAX_DEPTH: {type: "int", "default": 16},
|
MAX_DEPTH: { type: "int", default: 16 },
|
||||||
COOKIES_PATH: {type: "string"},
|
COOKIES_PATH: { type: "string" },
|
||||||
LOG_FORMAT: {type: "string", "default": "simple"},
|
LOG_FORMAT: { type: "string", default: "simple" },
|
||||||
ACCESS_LOG_FORMAT: {type: "string", "default": ""},
|
ACCESS_LOG_FORMAT: { type: "string", default: "" },
|
||||||
MAX_CONCURRENT_REQUESTS: {type: "int", "default": 20},
|
MAX_CONCURRENT_REQUESTS: { type: "int", default: 20 },
|
||||||
DATETIME_MILLISECONDS: {type: "bool", "default": true},
|
DATETIME_MILLISECONDS: { type: "bool", default: true },
|
||||||
BOOLEAN_LITERAL: {type: "bool", "default": true},
|
BOOLEAN_LITERAL: { type: "bool", default: true },
|
||||||
CONNECTION_REQUEST_ALLOW_BASIC_AUTH: {type: "bool", "default": false},
|
CONNECTION_REQUEST_ALLOW_BASIC_AUTH: { type: "bool", default: false },
|
||||||
MAX_COMMIT_ITERATIONS: {type: "int", "default": 32},
|
MAX_COMMIT_ITERATIONS: { type: "int", default: 32 },
|
||||||
|
|
||||||
// XML configuration
|
// XML configuration
|
||||||
XML_RECOVER: {type: "bool", "default": false},
|
XML_RECOVER: { type: "bool", default: false },
|
||||||
XML_IGNORE_ENC: {type: "bool", "default": false},
|
XML_IGNORE_ENC: { type: "bool", default: false },
|
||||||
XML_FORMAT: {type: "bool", "default": false},
|
XML_FORMAT: { type: "bool", default: false },
|
||||||
XML_NO_DECL: {type: "bool", "default": false},
|
XML_NO_DECL: { type: "bool", default: false },
|
||||||
XML_NO_EMPTY: {type: "bool", "default": false},
|
XML_NO_EMPTY: { type: "bool", default: false },
|
||||||
XML_IGNORE_NAMESPACE: {type: "bool", "default": false},
|
XML_IGNORE_NAMESPACE: { type: "bool", default: false },
|
||||||
|
|
||||||
// Should probably never be changed
|
// Should probably never be changed
|
||||||
DEVICE_ONLINE_THRESHOLD: {type: "int", "default": 4000}
|
DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 }
|
||||||
};
|
};
|
||||||
|
|
||||||
const allConfig = {};
|
const allConfig = {};
|
||||||
|
|
||||||
function setConfig(name, value, commandLineArgument) {
|
function setConfig(name, value, commandLineArgument) {
|
||||||
if (allConfig[name] != null)
|
if (allConfig[name] != null) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
// For compatibility with v1.0
|
// For compatibility with v1.0
|
||||||
if (name === "PRESETS_CACHE_DURATION" || name === "presets-cache-duration")
|
if (name === "PRESETS_CACHE_DURATION" || name === "presets-cache-duration")
|
||||||
setConfig("MAX_CACHE_TTL", value);
|
setConfig("MAX_CACHE_TTL", value);
|
||||||
|
|
||||||
if (name === "GET_PARAMETER_NAMES_DEPTH_THRESHOLD" || name === "get-parameter-names-depth-threshold")
|
if (
|
||||||
|
name === "GET_PARAMETER_NAMES_DEPTH_THRESHOLD" ||
|
||||||
|
name === "get-parameter-names-depth-threshold"
|
||||||
|
)
|
||||||
setConfig("GPN_NEXT_LEVEL", value);
|
setConfig("GPN_NEXT_LEVEL", value);
|
||||||
|
|
||||||
if (name === "TASK_PARAMETERS_BATCH_SIZE" || name === "task-parameters-batch-size")
|
if (
|
||||||
|
name === "TASK_PARAMETERS_BATCH_SIZE" ||
|
||||||
|
name === "task-parameters-batch-size"
|
||||||
|
)
|
||||||
setConfig("GPV_BATCH_SIZE", value);
|
setConfig("GPV_BATCH_SIZE", value);
|
||||||
|
|
||||||
if (name === "IGNORE_XML_NAMESPACES" || name === "ignore-xml-namespaces")
|
if (name === "IGNORE_XML_NAMESPACES" || name === "ignore-xml-namespaces")
|
||||||
@@ -107,20 +115,22 @@ function setConfig(name, value, commandLineArgument) {
|
|||||||
if (name === "XML_PARSE_RECOVER" || name === "xml-parse-recover")
|
if (name === "XML_PARSE_RECOVER" || name === "xml-parse-recover")
|
||||||
setConfig("XML_RECOVER", value);
|
setConfig("XML_RECOVER", value);
|
||||||
|
|
||||||
if (name === "FS_IP" || name === "fs-ip")
|
if (name === "FS_IP" || name === "fs-ip") setConfig("FS_HOSTNAME", value);
|
||||||
setConfig("FS_HOSTNAME", value);
|
|
||||||
|
|
||||||
function cast(val, type) {
|
function cast(val, type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "int":
|
case "int":
|
||||||
return Number(val);
|
return Number(val);
|
||||||
case "bool":
|
case "bool":
|
||||||
return ["true", "on", "yes", "1"].includes(String(val).trim().toLowerCase());
|
return ["true", "on", "yes", "1"].includes(
|
||||||
|
String(val)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
);
|
||||||
case "string":
|
case "string":
|
||||||
return String(val);
|
return String(val);
|
||||||
case "path":
|
case "path":
|
||||||
if (!val)
|
if (!val) return "";
|
||||||
return "";
|
|
||||||
return path.resolve(val);
|
return path.resolve(val);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@@ -130,8 +140,7 @@ function setConfig(name, value, commandLineArgument) {
|
|||||||
let _value = null;
|
let _value = null;
|
||||||
for (let [optionName, optionDetails] of Object.entries(options)) {
|
for (let [optionName, optionDetails] of Object.entries(options)) {
|
||||||
let n = optionName;
|
let n = optionName;
|
||||||
if (commandLineArgument)
|
if (commandLineArgument) n = n.toLowerCase().replace(/_/g, "-");
|
||||||
n = n.toLowerCase().replace(/_/g, "-");
|
|
||||||
|
|
||||||
if (name === n) {
|
if (name === n) {
|
||||||
_value = cast(value, optionDetails.type);
|
_value = cast(value, optionDetails.type);
|
||||||
@@ -163,55 +172,47 @@ while (argv.length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Environment variable
|
// Environment variable
|
||||||
for (const [k, v] of Object.entries(process.env)) {
|
for (const [k, v] of Object.entries(process.env))
|
||||||
if (k.startsWith("GENIEACS_"))
|
if (k.startsWith("GENIEACS_")) setConfig(k.slice(9), v);
|
||||||
setConfig(k.slice(9), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use default config dir if none defined
|
// Use default config dir if none defined
|
||||||
setConfig("CONFIG_DIR", options["CONFIG_DIR"]["default"]);
|
setConfig("CONFIG_DIR", options["CONFIG_DIR"]["default"]);
|
||||||
|
|
||||||
// Configuration file
|
// Configuration file
|
||||||
const configFile = require(path.resolve(allConfig.CONFIG_DIR, "config"));
|
const configFile = require(path.resolve(allConfig.CONFIG_DIR, "config"));
|
||||||
for (const [k, v] of Object.entries(configFile))
|
for (const [k, v] of Object.entries(configFile)) {
|
||||||
if (!setConfig(k, v))
|
if (!setConfig(k, v))
|
||||||
// Pass as environment variable to be accessable by extensions
|
// Pass as environment variable to be accessable by extensions
|
||||||
process.env[`GENIEACS_${k}`] = `${v}`;
|
process.env[`GENIEACS_${k}`] = `${v}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
for (const [k, v] of Object.entries(options))
|
for (const [k, v] of Object.entries(options))
|
||||||
if (v["default"] != null)
|
if (v["default"] != null) setConfig(k, v["default"]);
|
||||||
setConfig(k, v["default"]);
|
|
||||||
|
|
||||||
function get(optionName, deviceId) {
|
function get(optionName, deviceId) {
|
||||||
if (!deviceId)
|
if (!deviceId) return allConfig[optionName];
|
||||||
return allConfig[optionName];
|
|
||||||
|
|
||||||
optionName = `${optionName}-${deviceId}`;
|
optionName = `${optionName}-${deviceId}`;
|
||||||
let v = allConfig[optionName];
|
let v = allConfig[optionName];
|
||||||
if (v != null)
|
if (v != null) return v;
|
||||||
return v;
|
|
||||||
|
|
||||||
let i = optionName.lastIndexOf("-");
|
let i = optionName.lastIndexOf("-");
|
||||||
v = allConfig[optionName.slice(0, i)];
|
v = allConfig[optionName.slice(0, i)];
|
||||||
if (v != null)
|
if (v != null) return v;
|
||||||
return v;
|
|
||||||
|
|
||||||
i = optionName.lastIndexOf("-", i - 1);
|
i = optionName.lastIndexOf("-", i - 1);
|
||||||
v = allConfig[optionName.slice(0, i)];
|
v = allConfig[optionName.slice(0, i)];
|
||||||
if (v != null)
|
if (v != null) return v;
|
||||||
return v;
|
|
||||||
|
|
||||||
i = optionName.lastIndexOf("-", i - 1);
|
i = optionName.lastIndexOf("-", i - 1);
|
||||||
v = allConfig[optionName.slice(0, i)];
|
v = allConfig[optionName.slice(0, i)];
|
||||||
if (v != null)
|
if (v != null) return v;
|
||||||
return v;
|
|
||||||
|
|
||||||
i = optionName.lastIndexOf("-", i - 1);
|
i = optionName.lastIndexOf("-", i - 1);
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
v = allConfig[optionName.slice(0, i)];
|
v = allConfig[optionName.slice(0, i)];
|
||||||
if (v != null)
|
if (v != null) return v;
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -219,12 +220,10 @@ function get(optionName, deviceId) {
|
|||||||
|
|
||||||
function getDefault(optionName) {
|
function getDefault(optionName) {
|
||||||
const option = options[optionName];
|
const option = options[optionName];
|
||||||
if (!option)
|
if (!option) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
let val = option["default"];
|
let val = option["default"];
|
||||||
if (val && option.type === "path")
|
if (val && option.type === "path") val = path.resolve(val);
|
||||||
val = path.resolve(val);
|
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -232,7 +231,9 @@ function getDefault(optionName) {
|
|||||||
// Load authentication scripts
|
// Load authentication scripts
|
||||||
try {
|
try {
|
||||||
exports.auth = require(path.resolve(allConfig.CONFIG_DIR, "auth"));
|
exports.auth = require(path.resolve(allConfig.CONFIG_DIR, "auth"));
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
// No auth.js exists
|
||||||
|
}
|
||||||
|
|
||||||
exports.get = get;
|
exports.get = get;
|
||||||
exports.getDefault = getDefault;
|
exports.getDefault = getDefault;
|
||||||
+59
-31
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -25,8 +25,7 @@ const scheduling = require("./scheduling");
|
|||||||
|
|
||||||
const MAX_DEPTH = config.get("MAX_DEPTH");
|
const MAX_DEPTH = config.get("MAX_DEPTH");
|
||||||
|
|
||||||
|
function refresh(sessionContext, provision, declarations) {
|
||||||
function refresh(sessionContext, provision, declarations, startRevision, endRevision) {
|
|
||||||
let path = common.parsePath(provision[1]).slice();
|
let path = common.parsePath(provision[1]).slice();
|
||||||
let l = path.length;
|
let l = path.length;
|
||||||
path.length = MAX_DEPTH;
|
path.length = MAX_DEPTH;
|
||||||
@@ -35,70 +34,100 @@ function refresh(sessionContext, provision, declarations, startRevision, endRevi
|
|||||||
let offset = scheduling.variance(sessionContext.deviceId, every);
|
let offset = scheduling.variance(sessionContext.deviceId, every);
|
||||||
let t = scheduling.interval(sessionContext.timestamp, every, offset);
|
let t = scheduling.interval(sessionContext.timestamp, every, offset);
|
||||||
|
|
||||||
for (let i = l; i < path.length; ++ i)
|
for (let i = l; i < path.length; ++i) {
|
||||||
declarations.push([path.slice(0, i), t, {object: 1, writable: 1, value: t}]);
|
declarations.push([
|
||||||
|
path.slice(0, i),
|
||||||
|
t,
|
||||||
|
{ object: 1, writable: 1, value: t }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function value(sessionContext, provision, declarations) {
|
||||||
function value(sessionContext, provision, declarations, startRevision, endRevision) {
|
|
||||||
let path = common.parsePath(provision[1]);
|
let path = common.parsePath(provision[1]);
|
||||||
declarations.push([path, 1, {value: 1}, null, {value: [provision[2]]}]);
|
declarations.push([path, 1, { value: 1 }, null, { value: [provision[2]] }]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tag(sessionContext, provision, declarations) {
|
||||||
function tag(sessionContext, provision, declarations, startRevision, endRevision) {
|
|
||||||
let path = ["Tags", provision[1]];
|
let path = ["Tags", provision[1]];
|
||||||
declarations.push([path, 1, {value: 1}, null, {value: [provision[2]]}]);
|
declarations.push([path, 1, { value: 1 }, null, { value: [provision[2]] }]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reboot(sessionContext, provision, declarations) {
|
||||||
function reboot(sessionContext, provision, declarations, startRevision, endRevision) {
|
declarations.push([
|
||||||
declarations.push([["Reboot"], 1, {value: 1}, null, {value: [sessionContext.timestamp]}]);
|
["Reboot"],
|
||||||
|
1,
|
||||||
|
{ value: 1 },
|
||||||
|
null,
|
||||||
|
{ value: [sessionContext.timestamp] }
|
||||||
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reset(sessionContext, provision, declarations) {
|
||||||
function reset(sessionContext, provision, declarations, startRevision, endRevision) {
|
declarations.push([
|
||||||
declarations.push([["FactoryReset"], 1, {value: 1}, null, {value: [sessionContext.timestamp]}]);
|
["FactoryReset"],
|
||||||
|
1,
|
||||||
|
{ value: 1 },
|
||||||
|
null,
|
||||||
|
{ value: [sessionContext.timestamp] }
|
||||||
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function download(sessionContext, provision, declarations) {
|
||||||
function download(sessionContext, provision, declarations, startRevision, endRevision) {
|
let alias = [
|
||||||
let alias = [["FileType"], provision[1] || "", ["FileName"], provision[2] || "",
|
["FileType"],
|
||||||
["TargetFileName"], provision[3] || ""];
|
provision[1] || "",
|
||||||
|
["FileName"],
|
||||||
|
provision[2] || "",
|
||||||
|
["TargetFileName"],
|
||||||
|
provision[3] || ""
|
||||||
|
];
|
||||||
|
|
||||||
declarations.push([["Downloads", alias], 1, {}, 1]);
|
declarations.push([["Downloads", alias], 1, {}, 1]);
|
||||||
declarations.push([["Downloads", alias, "Download"], 1, {value: 1}, null,
|
declarations.push([
|
||||||
{value: [sessionContext.timestamp]}]);
|
["Downloads", alias, "Download"],
|
||||||
|
1,
|
||||||
|
{ value: 1 },
|
||||||
|
null,
|
||||||
|
{ value: [sessionContext.timestamp] }
|
||||||
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function instances(
|
||||||
function instances(sessionContext, provision, declarations, startRevision, endRevision) {
|
sessionContext,
|
||||||
|
provision,
|
||||||
|
declarations,
|
||||||
|
startRevision,
|
||||||
|
endRevision
|
||||||
|
) {
|
||||||
let count = Number(provision[2]);
|
let count = Number(provision[2]);
|
||||||
|
|
||||||
if (Number.isNaN(count))
|
if (Number.isNaN(count)) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
let path = common.parsePath(provision[1]);
|
let path = common.parsePath(provision[1]);
|
||||||
|
|
||||||
if (provision[2][0] === "+" || provision[2][0] === "-") {
|
if (provision[2][0] === "+" || provision[2][0] === "-") {
|
||||||
declarations.push([path, 1]);
|
declarations.push([path, 1]);
|
||||||
|
|
||||||
if (endRevision === startRevision)
|
if (endRevision === startRevision) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
let unpacked = device.unpack(sessionContext.deviceData, path, startRevision + 1);
|
let unpacked = device.unpack(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
path,
|
||||||
|
startRevision + 1
|
||||||
|
);
|
||||||
count = Math.max(0, unpacked.length + count);
|
count = Math.max(0, unpacked.length + count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +136,6 @@ function instances(sessionContext, provision, declarations, startRevision, endRe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.refresh = refresh;
|
exports.refresh = refresh;
|
||||||
exports.value = value;
|
exports.value = value;
|
||||||
exports.tag = tag;
|
exports.tag = tag;
|
||||||
|
|||||||
+133
-134
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -18,97 +18,88 @@
|
|||||||
*/
|
*/
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const common = require('./common');
|
|
||||||
|
|
||||||
|
|
||||||
const CHANGE_FLAGS = {
|
const CHANGE_FLAGS = {
|
||||||
object: 2,
|
object: 2,
|
||||||
writable: 4,
|
writable: 4,
|
||||||
value: 8
|
value: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function parseBool(v) {
|
function parseBool(v) {
|
||||||
v = "" + v;
|
v = "" + v;
|
||||||
if (v === "true" || v === "TRUE" || v === "True" || v === "1")
|
if (v === "true" || v === "TRUE" || v === "True" || v === "1") return true;
|
||||||
return true;
|
|
||||||
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
||||||
return false;
|
return false;
|
||||||
else
|
else return null;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function sanitizeParameterValue(parameterValue) {
|
function sanitizeParameterValue(parameterValue) {
|
||||||
if (parameterValue[0] != null) {
|
if (parameterValue[0] != null) {
|
||||||
switch (parameterValue[1]) {
|
switch (parameterValue[1]) {
|
||||||
case 'xsd:boolean':
|
case "xsd:boolean":
|
||||||
if (typeof parameterValue[0] !== 'boolean') {
|
if (typeof parameterValue[0] !== "boolean") {
|
||||||
let v = parseBool(parameterValue[0]);
|
let v = parseBool(parameterValue[0]);
|
||||||
if (v == null)
|
if (v == null) v = "" + parameterValue[0];
|
||||||
v = '' + parameterValue[0];
|
|
||||||
parameterValue[0] = v;
|
parameterValue[0] = v;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'xsd:string':
|
case "xsd:string":
|
||||||
case 'xsd:base64':
|
case "xsd:base64":
|
||||||
case 'xsd:hexBinary':
|
case "xsd:hexBinary":
|
||||||
if (typeof parameterValue[0] !== 'string') {
|
if (typeof parameterValue[0] !== "string")
|
||||||
parameterValue[0] = '' + parameterValue[0];
|
parameterValue[0] = "" + parameterValue[0];
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'xsd:int':
|
case "xsd:int":
|
||||||
case 'xsd:unsignedInt':
|
case "xsd:unsignedInt":
|
||||||
if (typeof parameterValue[0] !== 'number') {
|
if (typeof parameterValue[0] !== "number") {
|
||||||
let v = parseInt(parameterValue[0]);
|
let v = parseInt(parameterValue[0]);
|
||||||
if (isNaN(v))
|
if (isNaN(v)) v = "" + parameterValue[0];
|
||||||
v = '' + parameterValue[0];
|
|
||||||
parameterValue[0] = v;
|
parameterValue[0] = v;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'xsd:dateTime':
|
case "xsd:dateTime":
|
||||||
if (typeof parameterValue[0] !== 'number') {
|
if (typeof parameterValue[0] !== "number") {
|
||||||
// Don't use parseInt because it reads date string as a number
|
// Don't use parseInt because it reads date string as a number
|
||||||
let v = +parameterValue[0];
|
let v = +parameterValue[0];
|
||||||
if (isNaN(v)) {
|
if (isNaN(v)) {
|
||||||
v = Date.parse(parameterValue[0]);
|
v = Date.parse(parameterValue[0]);
|
||||||
if (isNaN(v))
|
if (isNaN(v)) v = "" + parameterValue[0];
|
||||||
v = '' + parameterValue[0];
|
|
||||||
}
|
}
|
||||||
parameterValue[0] = v;
|
parameterValue[0] = v;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (parameterValue[1] != null)
|
if (parameterValue[1] != null) {
|
||||||
throw new Error(`Parameter value type "${parameterValue[1]}" not recognized.`);
|
throw new Error(
|
||||||
|
`Parameter value type "${parameterValue[1]}" not recognized.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
parameterValue[0] = JSON.parse(JSON.stringify(parameterValue[0]));
|
parameterValue[0] = JSON.parse(JSON.stringify(parameterValue[0]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameterValue;
|
return parameterValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getAliasDeclarations(path, timestamp) {
|
function getAliasDeclarations(path, timestamp) {
|
||||||
var decs = [];
|
var decs = [];
|
||||||
|
|
||||||
function recursive(pattern, prefix) {
|
function recursive(p, prefix) {
|
||||||
var pattern = prefix.concat(pattern);
|
const pattern = prefix.concat(p);
|
||||||
|
|
||||||
for (let i = prefix.length; i < pattern.length; ++ i) {
|
for (let i = prefix.length; i < pattern.length; ++i) {
|
||||||
if (Array.isArray(pattern[i])) {
|
if (Array.isArray(pattern[i])) {
|
||||||
let pat = pattern[i];
|
let pat = pattern[i];
|
||||||
pattern[i] = '*';
|
pattern[i] = "*";
|
||||||
for (let j = 0; j < pat.length; j += 2)
|
for (let j = 0; j < pat.length; j += 2)
|
||||||
recursive(pat[j], pattern.slice(0, i + 1));
|
recursive(pat[j], pattern.slice(0, i + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefix.length)
|
if (prefix.length) decs.push([pattern, timestamp, { value: timestamp }]);
|
||||||
decs.push([pattern, timestamp, {value: timestamp}]);
|
else decs.push([pattern, timestamp]);
|
||||||
else
|
|
||||||
decs.push([pattern, timestamp]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recursive(path, []);
|
recursive(path, []);
|
||||||
@@ -116,44 +107,51 @@ function getAliasDeclarations(path, timestamp) {
|
|||||||
return decs;
|
return decs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function unpack(deviceData, path, revision) {
|
function unpack(deviceData, path, revision) {
|
||||||
let allMatches = [];
|
let allMatches = [];
|
||||||
if (!path.alias) {
|
if (!path.alias) {
|
||||||
for (let p of deviceData.paths.find(path, false, true))
|
for (let p of deviceData.paths.find(path, false, true))
|
||||||
if (deviceData.attributes.has(p, revision))
|
if (deviceData.attributes.has(p, revision)) allMatches.push(p);
|
||||||
allMatches.push(p);
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
let wildcardPath = path.slice();
|
let wildcardPath = path.slice();
|
||||||
wildcardPath.wildcard = path.wildcard;
|
wildcardPath.wildcard = path.wildcard;
|
||||||
for (let i = 0; i < wildcardPath.length; ++ i)
|
for (let i = 0; i < wildcardPath.length; ++i) {
|
||||||
if (Array.isArray(wildcardPath[i])) {
|
if (Array.isArray(wildcardPath[i])) {
|
||||||
wildcardPath[i] = '*';
|
wildcardPath[i] = "*";
|
||||||
wildcardPath.wildcard |= 1 << i;
|
wildcardPath.wildcard |= 1 << i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let p of deviceData.paths.find(wildcardPath, false, true))
|
for (let p of deviceData.paths.find(wildcardPath, false, true))
|
||||||
if (deviceData.attributes.has(p, revision))
|
if (deviceData.attributes.has(p, revision)) allMatches.push(p);
|
||||||
allMatches.push(p);
|
|
||||||
|
|
||||||
for (let i = path.length - 1; i >= 0; -- i) {
|
for (let i = path.length - 1; i >= 0; --i) {
|
||||||
if (Array.isArray(path[i])) {
|
if (Array.isArray(path[i])) {
|
||||||
for (let j = 0; j < path[i].length; j += 2) {
|
for (let j = 0; j < path[i].length; j += 2) {
|
||||||
let p = wildcardPath.slice(0, i + 1).concat(path[i][j]);
|
let p = wildcardPath.slice(0, i + 1).concat(path[i][j]);
|
||||||
p.alias = (wildcardPath.alias & ((1 << (i + 1)) - 1)) + path[i][j].alias << (i + 1);
|
p.alias =
|
||||||
p.wildcard = (wildcardPath.wildcard & ((1 << (i + 1)) - 1)) + path[i][j].wildcard << (i + 1);
|
((wildcardPath.alias & ((1 << (i + 1)) - 1)) + path[i][j].alias) <<
|
||||||
|
(i + 1);
|
||||||
|
p.wildcard =
|
||||||
|
((wildcardPath.wildcard & ((1 << (i + 1)) - 1)) +
|
||||||
|
path[i][j].wildcard) <<
|
||||||
|
(i + 1);
|
||||||
let unpacked = unpack(deviceData, p, revision);
|
let unpacked = unpack(deviceData, p, revision);
|
||||||
let filtered = [];
|
let filtered = [];
|
||||||
for (let up of unpacked) {
|
for (let up of unpacked) {
|
||||||
let attributes = deviceData.attributes.get(up, revision);
|
let attributes = deviceData.attributes.get(up, revision);
|
||||||
if (attributes && attributes.value && attributes.value[1] &&
|
if (
|
||||||
sanitizeParameterValue([path[i][j + 1], attributes.value[1][1]])[0] === attributes.value[1][0]) {
|
attributes &&
|
||||||
for (let m = 0; m < allMatches.length; ++ m) {
|
attributes.value &&
|
||||||
|
attributes.value[1] &&
|
||||||
|
sanitizeParameterValue([
|
||||||
|
path[i][j + 1],
|
||||||
|
attributes.value[1][1]
|
||||||
|
])[0] === attributes.value[1][0]
|
||||||
|
) {
|
||||||
|
for (let m = 0; m < allMatches.length; ++m) {
|
||||||
let k;
|
let k;
|
||||||
for (k = i; k >= 0; -- k)
|
for (k = i; k >= 0; --k) if (allMatches[m][k] != up[k]) break;
|
||||||
if (allMatches[m][k] != up[k])
|
|
||||||
break
|
|
||||||
|
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
filtered.push(allMatches[m]);
|
filtered.push(allMatches[m]);
|
||||||
@@ -168,22 +166,20 @@ function unpack(deviceData, path, revision) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allMatches.sort(function(a, b) {
|
allMatches.sort((a, b) => {
|
||||||
for (let i = 0; i < a.length; ++ i) {
|
for (let i = 0; i < a.length; ++i) {
|
||||||
if (a === b)
|
if (a === b) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
// Use numeric sorting for numbers
|
// Use numeric sorting for numbers
|
||||||
let ia = parseInt(a), ib = parseInt(b);
|
let ia = parseInt(a),
|
||||||
|
ib = parseInt(b);
|
||||||
if (ia.toString() === a && ib.toString() === b) {
|
if (ia.toString() === a && ib.toString() === b) {
|
||||||
a = ia;
|
a = ia;
|
||||||
b = ib;
|
b = ib;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a < b)
|
if (a < b) return -1;
|
||||||
return -1;
|
else return 1;
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
@@ -191,7 +187,6 @@ function unpack(deviceData, path, revision) {
|
|||||||
return allMatches;
|
return allMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
|
function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
|
||||||
let changeTrackers = {};
|
let changeTrackers = {};
|
||||||
|
|
||||||
@@ -205,28 +200,34 @@ function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
|
|||||||
attributes.value = attributes.object;
|
attributes.value = attributes.object;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let p of deviceData.paths.find(path, true, true, descendantsTimestamp ? 99 : path.length)) {
|
for (let p of deviceData.paths.find(
|
||||||
|
path,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
descendantsTimestamp ? 99 : path.length
|
||||||
|
)) {
|
||||||
let tracker = deviceData.trackers.get(p);
|
let tracker = deviceData.trackers.get(p);
|
||||||
for (let k in tracker)
|
for (let k in tracker) changeTrackers[k] |= tracker[k];
|
||||||
changeTrackers[k] |= tracker[k];
|
|
||||||
|
|
||||||
let currentTimestamp = deviceData.timestamps.get(p);
|
let currentTimestamp = deviceData.timestamps.get(p);
|
||||||
if (currentTimestamp === undefined)
|
if (currentTimestamp === undefined) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (timestamp > currentTimestamp ||
|
if (
|
||||||
(descendantsTimestamp > currentTimestamp && p.length > path.length)) {
|
timestamp > currentTimestamp ||
|
||||||
|
(descendantsTimestamp > currentTimestamp && p.length > path.length)
|
||||||
|
) {
|
||||||
deviceData.timestamps.delete(p);
|
deviceData.timestamps.delete(p);
|
||||||
deviceData.attributes.delete(p);
|
deviceData.attributes.delete(p);
|
||||||
changeFlags |= 1;
|
changeFlags |= 1;
|
||||||
}
|
} else if (attributes && p.length == path.length) {
|
||||||
else if (attributes && p.length == path.length) {
|
|
||||||
let currentAttributes = deviceData.attributes.get(p);
|
let currentAttributes = deviceData.attributes.get(p);
|
||||||
if (currentAttributes) {
|
if (currentAttributes) {
|
||||||
let newAttrs;
|
let newAttrs;
|
||||||
for (let attrName in attributes) {
|
for (let attrName in attributes) {
|
||||||
if (attrName in currentAttributes &&
|
if (
|
||||||
attributes[attrName] > currentAttributes[attrName][0]) {
|
attrName in currentAttributes &&
|
||||||
|
attributes[attrName] > currentAttributes[attrName][0]
|
||||||
|
) {
|
||||||
changeFlags |= CHANGE_FLAGS[attrName];
|
changeFlags |= CHANGE_FLAGS[attrName];
|
||||||
if (!newAttrs) {
|
if (!newAttrs) {
|
||||||
newAttrs = Object.assign({}, currentAttributes);
|
newAttrs = Object.assign({}, currentAttributes);
|
||||||
@@ -241,27 +242,25 @@ function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
|
|||||||
|
|
||||||
// Note: For performance, we're merging all changes together rather than
|
// Note: For performance, we're merging all changes together rather than
|
||||||
// mark changes based the exact parameters affected.
|
// mark changes based the exact parameters affected.
|
||||||
for (let k in changeTrackers) {
|
for (let k in changeTrackers)
|
||||||
if (changeTrackers[k] & changeFlags)
|
if (changeTrackers[k] & changeFlags) deviceData.changes.add(k);
|
||||||
deviceData.changes.add(k);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function compareEquality(a, b) {
|
function compareEquality(a, b) {
|
||||||
var t = typeof a;
|
var t = typeof a;
|
||||||
if (a === null ||
|
if (
|
||||||
a === undefined ||
|
a === null ||
|
||||||
t === 'number' ||
|
a === undefined ||
|
||||||
t === 'boolean' ||
|
t === "number" ||
|
||||||
t === 'string' ||
|
t === "boolean" ||
|
||||||
t === 'symbol')
|
t === "string" ||
|
||||||
|
t === "symbol"
|
||||||
|
)
|
||||||
return a === b;
|
return a === b;
|
||||||
|
|
||||||
return JSON.stringify(a) === JSON.stringify(b);
|
return JSON.stringify(a) === JSON.stringify(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function set(deviceData, path, timestamp, attributes, toClear) {
|
function set(deviceData, path, timestamp, attributes, toClear) {
|
||||||
path = deviceData.paths.add(path);
|
path = deviceData.paths.add(path);
|
||||||
|
|
||||||
@@ -269,20 +268,25 @@ function set(deviceData, path, timestamp, attributes, toClear) {
|
|||||||
|
|
||||||
let currentAttributes;
|
let currentAttributes;
|
||||||
|
|
||||||
if (path.wildcard)
|
if (path.wildcard) attributes = undefined;
|
||||||
attributes = undefined;
|
|
||||||
else if (currentTimestamp)
|
else if (currentTimestamp)
|
||||||
currentAttributes = deviceData.attributes.get(path);
|
currentAttributes = deviceData.attributes.get(path);
|
||||||
|
|
||||||
let changeFlags = 0;
|
let changeFlags = 0;
|
||||||
|
|
||||||
if (attributes) {
|
if (attributes) {
|
||||||
if (attributes.value && attributes.value[1]
|
if (
|
||||||
&& attributes.value[0] >= (attributes.object ? attributes.object[0] : 0))
|
attributes.value &&
|
||||||
|
attributes.value[1] &&
|
||||||
|
attributes.value[0] >= (attributes.object ? attributes.object[0] : 0)
|
||||||
|
)
|
||||||
attributes.object = [attributes.value[0], 0];
|
attributes.object = [attributes.value[0], 0];
|
||||||
|
|
||||||
if (attributes.object && attributes.object[1]
|
if (
|
||||||
&& attributes.object[0] >= (attributes.value ? attributes.value[0] : 0))
|
attributes.object &&
|
||||||
|
attributes.object[1] &&
|
||||||
|
attributes.object[0] >= (attributes.value ? attributes.value[0] : 0)
|
||||||
|
)
|
||||||
attributes.value = [attributes.object[0]];
|
attributes.value = [attributes.object[0]];
|
||||||
|
|
||||||
let newAttributes = Object.assign({}, currentAttributes, attributes);
|
let newAttributes = Object.assign({}, currentAttributes, attributes);
|
||||||
@@ -294,35 +298,45 @@ function set(deviceData, path, timestamp, attributes, toClear) {
|
|||||||
changeFlags |= CHANGE_FLAGS[attrName];
|
changeFlags |= CHANGE_FLAGS[attrName];
|
||||||
else if (attributes[attrName][0] <= currentAttributes[attrName][0])
|
else if (attributes[attrName][0] <= currentAttributes[attrName][0])
|
||||||
newAttributes[attrName] = currentAttributes[attrName];
|
newAttributes[attrName] = currentAttributes[attrName];
|
||||||
else if (!compareEquality(attributes[attrName][1], currentAttributes[attrName][1]))
|
else if (
|
||||||
|
!compareEquality(
|
||||||
|
attributes[attrName][1],
|
||||||
|
currentAttributes[attrName][1]
|
||||||
|
)
|
||||||
|
)
|
||||||
changeFlags |= CHANGE_FLAGS[attrName];
|
changeFlags |= CHANGE_FLAGS[attrName];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
changeFlags |= 1;
|
changeFlags |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
deviceData.attributes.set(path, newAttributes);
|
deviceData.attributes.set(path, newAttributes);
|
||||||
|
|
||||||
if (!(timestamp <= currentTimestamp)) {
|
if (!(timestamp <= currentTimestamp)) {
|
||||||
deviceData.timestamps.set(path, timestamp);
|
deviceData.timestamps.set(path, timestamp);
|
||||||
if (path.length > 1)
|
if (path.length > 1) {
|
||||||
toClear = set(deviceData, path.slice(0, path.length - 1), timestamp,
|
toClear = set(
|
||||||
{object: [timestamp, 1]}, toClear);
|
deviceData,
|
||||||
|
path.slice(0, path.length - 1),
|
||||||
|
timestamp,
|
||||||
|
{ object: [timestamp, 1] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (!(timestamp <= currentTimestamp)) {
|
||||||
else if (!(timestamp <= currentTimestamp)) {
|
|
||||||
deviceData.timestamps.set(path, timestamp);
|
deviceData.timestamps.set(path, timestamp);
|
||||||
|
|
||||||
if (currentAttributes) {
|
if (currentAttributes) {
|
||||||
deviceData.attributes.delete(path);
|
deviceData.attributes.delete(path);
|
||||||
changeFlags |= 1;
|
changeFlags |= 1;
|
||||||
}
|
} else if (path.wildcard) {
|
||||||
else if (path.wildcard) {
|
for (let p of deviceData.paths.find(path, false, true, path.length)) {
|
||||||
for (let p of deviceData.paths.find(path, false, true, path.length))
|
|
||||||
if (timestamp > deviceData.timestamps.get(p)) {
|
if (timestamp > deviceData.timestamps.get(p)) {
|
||||||
toClear = toClear || [];
|
toClear = toClear || [];
|
||||||
toClear.push([p, timestamp]);
|
toClear.push([p, timestamp]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,18 +344,14 @@ function set(deviceData, path, timestamp, attributes, toClear) {
|
|||||||
if (changeFlags & 1) {
|
if (changeFlags & 1) {
|
||||||
toClear = toClear || [];
|
toClear = toClear || [];
|
||||||
toClear.push([path, timestamp, null, changeFlags]);
|
toClear.push([path, timestamp, null, changeFlags]);
|
||||||
}
|
} else if (changeFlags & CHANGE_FLAGS.object) {
|
||||||
else if (changeFlags & CHANGE_FLAGS.object) {
|
|
||||||
toClear = toClear || [];
|
toClear = toClear || [];
|
||||||
toClear.push([path, 0, {object: attributes.object[0]}, changeFlags]);
|
toClear.push([path, 0, { object: attributes.object[0] }, changeFlags]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
for (let p of deviceData.paths.find(path, true, false, path.length)) {
|
for (let p of deviceData.paths.find(path, true, false, path.length)) {
|
||||||
let tracker = deviceData.trackers.get(p);
|
let tracker = deviceData.trackers.get(p);
|
||||||
for (let k in tracker) {
|
for (let k in tracker)
|
||||||
if (tracker[k] & changeFlags)
|
if (tracker[k] & changeFlags) deviceData.changes.add(k);
|
||||||
deviceData.changes.add(k);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,14 +359,11 @@ function set(deviceData, path, timestamp, attributes, toClear) {
|
|||||||
return toClear;
|
return toClear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function track(deviceData, path, marker, attributes) {
|
function track(deviceData, path, marker, attributes) {
|
||||||
path = deviceData.paths.add(path);
|
path = deviceData.paths.add(path);
|
||||||
let f = 1;
|
let f = 1;
|
||||||
|
|
||||||
if (attributes)
|
if (attributes) for (let attrName of attributes) f |= CHANGE_FLAGS[attrName];
|
||||||
for (let attrName of attributes)
|
|
||||||
f |= CHANGE_FLAGS[attrName];
|
|
||||||
|
|
||||||
let cur = deviceData.trackers.get(path);
|
let cur = deviceData.trackers.get(path);
|
||||||
if (!cur) {
|
if (!cur) {
|
||||||
@@ -367,25 +374,17 @@ function track(deviceData, path, marker, attributes) {
|
|||||||
cur[marker] |= f;
|
cur[marker] |= f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function clearTrackers(deviceData, tracker) {
|
function clearTrackers(deviceData, tracker) {
|
||||||
if (Array.isArray(tracker)) {
|
if (Array.isArray(tracker)) {
|
||||||
deviceData.trackers.forEach(function(v, k) {
|
for (let v of deviceData.trackers.values())
|
||||||
for (let t of tracker)
|
for (let t of tracker) delete v[t];
|
||||||
delete v[t];
|
for (let t of tracker) deviceData.changes.delete(t);
|
||||||
});
|
} else {
|
||||||
for (let t of tracker)
|
for (let v of deviceData.trackers.values()) delete v[tracker];
|
||||||
deviceData.changes.delete(t);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
deviceData.trackers.forEach(function(v, k) {
|
|
||||||
delete v[tracker];
|
|
||||||
});
|
|
||||||
deviceData.changes.delete(tracker);
|
deviceData.changes.delete(tracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.sanitizeParameterValue = sanitizeParameterValue;
|
exports.sanitizeParameterValue = sanitizeParameterValue;
|
||||||
exports.getAliasDeclarations = getAliasDeclarations;
|
exports.getAliasDeclarations = getAliasDeclarations;
|
||||||
exports.unpack = unpack;
|
exports.unpack = unpack;
|
||||||
|
|||||||
+12
-13
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -23,11 +23,9 @@ const fileName = process.argv[2];
|
|||||||
let script;
|
let script;
|
||||||
|
|
||||||
function errorToFault(err) {
|
function errorToFault(err) {
|
||||||
if (!err)
|
if (!err) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!err.name)
|
if (!err.name) return { code: "ext", message: `${err}` };
|
||||||
return {code: "ext", message: `${err}`};
|
|
||||||
|
|
||||||
let fault = {
|
let fault = {
|
||||||
code: `ext.${err.name}`,
|
code: `ext.${err.name}`,
|
||||||
@@ -41,7 +39,9 @@ function errorToFault(err) {
|
|||||||
if (err.stack) {
|
if (err.stack) {
|
||||||
fault.detail.stack = err.stack;
|
fault.detail.stack = err.stack;
|
||||||
// Trim the stack trace
|
// Trim the stack trace
|
||||||
let stackTrimIndex = fault.detail.stack.match(/\s+at\s[^\s]+\s\(.*extension-wrapper\.js:.+\)/);
|
let stackTrimIndex = fault.detail.stack.match(
|
||||||
|
/\s+at\s[^\s]+\s\(.*extension-wrapper\.js:.+\)/
|
||||||
|
);
|
||||||
if (stackTrimIndex)
|
if (stackTrimIndex)
|
||||||
fault.detail.stack = fault.detail.stack.slice(0, stackTrimIndex.index);
|
fault.detail.stack = fault.detail.stack.slice(0, stackTrimIndex.index);
|
||||||
}
|
}
|
||||||
@@ -49,16 +49,16 @@ function errorToFault(err) {
|
|||||||
return fault;
|
return fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on("uncaughtException", function(err) {
|
process.on("uncaughtException", err => {
|
||||||
let fault = errorToFault(err);
|
let fault = errorToFault(err);
|
||||||
jobs.forEach(function(jobId) {
|
jobs.forEach(jobId => {
|
||||||
process.send([jobId, fault]);
|
process.send([jobId, fault]);
|
||||||
});
|
});
|
||||||
jobs.clear();
|
jobs.clear();
|
||||||
process.disconnect();
|
process.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("message", function(message) {
|
process.on("message", message => {
|
||||||
jobs.add(message[0]);
|
jobs.add(message[0]);
|
||||||
|
|
||||||
if (!script) {
|
if (!script) {
|
||||||
@@ -78,13 +78,12 @@ process.on("message", function(message) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
script[funcName](message[1].slice(1), function(err, res) {
|
script[funcName](message[1].slice(1), (err, res) => {
|
||||||
if (!jobs.delete(message[0]))
|
if (!jobs.delete(message[0])) return;
|
||||||
return;
|
|
||||||
|
|
||||||
process.send([message[0], errorToFault(err), res]);
|
process.send([message[0], errorToFault(err), res]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ignore SIGINT
|
// Ignore SIGINT
|
||||||
process.on('SIGINT', function() {});
|
process.on("SIGINT", () => {});
|
||||||
+23
-30
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -19,11 +19,11 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const childProcess = require("child_process");
|
const childProcess = require("child_process");
|
||||||
const crypto = require ("crypto");
|
const crypto = require("crypto");
|
||||||
|
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
|
|
||||||
const TIMEOUT = config.get('EXT_TIMEOUT');
|
const TIMEOUT = config.get("EXT_TIMEOUT");
|
||||||
|
|
||||||
const processes = {};
|
const processes = {};
|
||||||
const jobs = {};
|
const jobs = {};
|
||||||
@@ -36,82 +36,75 @@ function messageHandler(message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function run(args, callback) {
|
function run(args, callback) {
|
||||||
let scriptName = args[0];
|
let scriptName = args[0];
|
||||||
|
|
||||||
if (!processes[scriptName]) {
|
if (!processes[scriptName]) {
|
||||||
let p = childProcess.fork('lib/extension-wrapper', [scriptName]);
|
let p = childProcess.fork("lib/extension-wrapper", [scriptName]);
|
||||||
processes[scriptName] = p;
|
processes[scriptName] = p;
|
||||||
|
|
||||||
p.on('error', function() {
|
p.on("error", () => {
|
||||||
if (processes[scriptName] === p) {
|
if (processes[scriptName] === p) {
|
||||||
kill(processes[scriptName]);
|
kill(processes[scriptName]);
|
||||||
delete processes[scriptName];
|
delete processes[scriptName];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
p.on('disconnect', function() {
|
p.on("disconnect", () => {
|
||||||
if (processes[scriptName] === p) {
|
if (processes[scriptName] === p) {
|
||||||
kill(processes[scriptName]);
|
kill(processes[scriptName]);
|
||||||
delete processes[scriptName];
|
delete processes[scriptName];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
p.on('message', messageHandler);
|
p.on("message", messageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = crypto.randomBytes(8).toString('hex');
|
let id = crypto.randomBytes(8).toString("hex");
|
||||||
jobs[id] = callback;
|
jobs[id] = callback;
|
||||||
setTimeout(function() {
|
setTimeout(() => {
|
||||||
if (id in jobs) {
|
if (id in jobs) {
|
||||||
delete jobs[id];
|
delete jobs[id];
|
||||||
return callback(null, {code: "timeout", message: "Extension timed out"});
|
return callback(null, {
|
||||||
|
code: "timeout",
|
||||||
|
message: "Extension timed out"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
|
|
||||||
return processes[scriptName].send([id, args.slice(1)]);
|
return processes[scriptName].send([id, args.slice(1)]);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
function kill(process, callback) {
|
function kill(process, callback) {
|
||||||
if (process.signalCode !== null || process.exitCode !== null)
|
if (process.signalCode !== null || process.exitCode !== null) return;
|
||||||
return;
|
|
||||||
|
|
||||||
let timeToKill = Date.now() + 5000;
|
let timeToKill = Date.now() + 5000;
|
||||||
|
|
||||||
process.kill();
|
process.kill();
|
||||||
|
|
||||||
let t = setInterval(function() {
|
let t = setInterval(() => {
|
||||||
if (process.signalCode !== null || process.exitCode !== null) {
|
if (process.signalCode !== null || process.exitCode !== null) {
|
||||||
clearInterval(t);
|
clearInterval(t);
|
||||||
if (callback)
|
if (callback) callback();
|
||||||
callback();
|
} else if (Date.now() > timeToKill) {
|
||||||
}
|
process.kill("SIGKILL");
|
||||||
else if (Date.now() > timeToKill) {
|
|
||||||
process.kill('SIGKILL');
|
|
||||||
clearInterval(t);
|
clearInterval(t);
|
||||||
if (callback)
|
if (callback) callback();
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function killAll(callback) {
|
function killAll(callback) {
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (let n in processes) {
|
for (let n in processes) {
|
||||||
++counter;
|
++counter;
|
||||||
kill(processes[n], function() {
|
kill(processes[n], () => {
|
||||||
if (--counter == 0 && callback)
|
if (--counter == 0 && callback) callback();
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
delete processes[n];
|
delete processes[n];
|
||||||
}
|
}
|
||||||
if (--counter == 0 && callback)
|
if (--counter == 0 && callback) callback();
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.run = run;
|
exports.run = run;
|
||||||
exports.killAll = killAll;
|
exports.killAll = killAll;
|
||||||
@@ -28,7 +28,7 @@ function listener(request, response) {
|
|||||||
if (request.method === "GET") {
|
if (request.method === "GET") {
|
||||||
const filename = querystring.unescape(urlParts.pathname.substring(1));
|
const filename = querystring.unescape(urlParts.pathname.substring(1));
|
||||||
const gs = new mongodb.GridStore(db.mongoDb, filename, "r", {});
|
const gs = new mongodb.GridStore(db.mongoDb, filename, "r", {});
|
||||||
gs.open((err, gs) => {
|
gs.open(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end();
|
response.end();
|
||||||
@@ -42,9 +42,7 @@ function listener(request, response) {
|
|||||||
stream.pipe(response);
|
stream.pipe(response);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {
|
response.writeHead(405, { Allow: "GET" });
|
||||||
"Allow": "GET"
|
|
||||||
});
|
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-11
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -41,32 +41,31 @@ function estimateGpnCount(gpnPatterns, depth = 0) {
|
|||||||
let k = path[depth];
|
let k = path[depth];
|
||||||
|
|
||||||
if (!k) {
|
if (!k) {
|
||||||
if (flags & 1)
|
if (flags & 1) gpnCount = 1;
|
||||||
gpnCount = 1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & 1) {
|
if (flags & 1) {
|
||||||
gpnCount = 1;
|
gpnCount = 1;
|
||||||
|
|
||||||
if (depth > UNDISCOVERED_DEPTH)
|
if (depth > UNDISCOVERED_DEPTH) continue;
|
||||||
continue;
|
} else if (k === "*") {
|
||||||
}
|
|
||||||
else if (k === "*")
|
|
||||||
wildcardDiscovered = true;
|
wildcardDiscovered = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (k === "*") {
|
if (k === "*") {
|
||||||
wildcardChildren.push(pattern);
|
wildcardChildren.push(pattern);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
children[k] = children[k] || [];
|
children[k] = children[k] || [];
|
||||||
children[k].push(pattern);
|
children[k].push(pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let wildcardGpnCount = 0;
|
let wildcardGpnCount = 0;
|
||||||
if (!wildcardDiscovered && wildcardChildren.length)
|
if (!wildcardDiscovered && wildcardChildren.length) {
|
||||||
wildcardGpnCount += estimateGpnCount(wildcardChildren, depth + 1) * WILDCARD_MULTIPLIER;
|
wildcardGpnCount +=
|
||||||
|
estimateGpnCount(wildcardChildren, depth + 1) * WILDCARD_MULTIPLIER;
|
||||||
|
}
|
||||||
|
|
||||||
for (let k of Object.keys(children)) {
|
for (let k of Object.keys(children)) {
|
||||||
let c = estimateGpnCount(children[k].concat(wildcardChildren), depth + 1);
|
let c = estimateGpnCount(children[k].concat(wildcardChildren), depth + 1);
|
||||||
|
|||||||
+17
-31
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -18,9 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
class InstanceSet {
|
class InstanceSet {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.set = new Set();
|
this.set = new Set();
|
||||||
}
|
}
|
||||||
@@ -44,29 +42,23 @@ class InstanceSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match)
|
if (match) res.push(inst);
|
||||||
res.push(inst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sort(function(a, b) {
|
res.sort((a, b) => {
|
||||||
let keysA = Object.keys(a);
|
let keysA = Object.keys(a);
|
||||||
let keysB = Object.keys(b);
|
let keysB = Object.keys(b);
|
||||||
|
|
||||||
if (keysA.length != keysB.length)
|
if (keysA.length != keysB.length) return keysB.length - keysA.length;
|
||||||
return keysB.length - keysA.length;
|
|
||||||
|
|
||||||
keysA.sort();
|
keysA.sort();
|
||||||
keysB.sort();
|
keysB.sort();
|
||||||
|
|
||||||
for (let i = 0; i < keysA.length; ++ i) {
|
for (let i = 0; i < keysA.length; ++i) {
|
||||||
if (keysA[i] > keysB[i])
|
if (keysA[i] > keysB[i]) return 1;
|
||||||
return 1;
|
else if (keysA[i] < keysB[i]) return -1;
|
||||||
else if (keysA[i] < keysB[i])
|
else if (a[keysA[i]] > b[keysB[i]]) return 1;
|
||||||
return -1;
|
else if (a[keysA[i]] < b[keysB[i]]) return -1;
|
||||||
else if (a[keysA[i]] > b[keysB[i]])
|
|
||||||
return 1;
|
|
||||||
else if (a[keysA[i]] < b[keysB[i]])
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -87,29 +79,23 @@ class InstanceSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match)
|
if (match) res.push(inst);
|
||||||
res.push(inst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sort(function(a, b) {
|
res.sort((a, b) => {
|
||||||
let keysA = Object.keys(a);
|
let keysA = Object.keys(a);
|
||||||
let keysB = Object.keys(b);
|
let keysB = Object.keys(b);
|
||||||
|
|
||||||
if (keysA.length != keysB.length)
|
if (keysA.length != keysB.length) return keysA.length - keysB.length;
|
||||||
return keysA.length - keysB.length;
|
|
||||||
|
|
||||||
keysA.sort();
|
keysA.sort();
|
||||||
keysB.sort();
|
keysB.sort();
|
||||||
|
|
||||||
for (let i = 0; i < keysA.length; ++ i) {
|
for (let i = 0; i < keysA.length; ++i) {
|
||||||
if (keysA[i] > keysB[i])
|
if (keysA[i] > keysB[i]) return 1;
|
||||||
return 1;
|
else if (keysA[i] < keysB[i]) return -1;
|
||||||
else if (keysA[i] < keysB[i])
|
else if (a[keysA[i]] > b[keysB[i]]) return 1;
|
||||||
return -1;
|
else if (a[keysA[i]] < b[keysB[i]]) return -1;
|
||||||
else if (a[keysA[i]] > b[keysB[i]])
|
|
||||||
return 1;
|
|
||||||
else if (a[keysA[i]] < b[keysB[i]])
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+85
-76
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -33,7 +33,6 @@ let nextRefresh = 1;
|
|||||||
let hash = null;
|
let hash = null;
|
||||||
let presets, provisions, virtualParameters, files;
|
let presets, provisions, virtualParameters, files;
|
||||||
|
|
||||||
|
|
||||||
function computeHash() {
|
function computeHash() {
|
||||||
// MD5 hash for presets, provisions, virtual parameters for detecting changes
|
// MD5 hash for presets, provisions, virtual parameters for detecting changes
|
||||||
let h = crypto.createHash("md5");
|
let h = crypto.createHash("md5");
|
||||||
@@ -50,13 +49,11 @@ function computeHash() {
|
|||||||
|
|
||||||
keys = Object.keys(provisions).sort();
|
keys = Object.keys(provisions).sort();
|
||||||
h.update(JSON.stringify(keys));
|
h.update(JSON.stringify(keys));
|
||||||
for (let k of keys)
|
for (let k of keys) h.update(provisions[k].md5);
|
||||||
h.update(provisions[k].md5);
|
|
||||||
|
|
||||||
keys = Object.keys(virtualParameters).sort();
|
keys = Object.keys(virtualParameters).sort();
|
||||||
h.update(JSON.stringify(keys));
|
h.update(JSON.stringify(keys));
|
||||||
for (let k of keys)
|
for (let k of keys) h.update(virtualParameters[k].md5);
|
||||||
h.update(virtualParameters[k].md5);
|
|
||||||
|
|
||||||
hash = h.digest("hex");
|
hash = h.digest("hex");
|
||||||
}
|
}
|
||||||
@@ -66,47 +63,45 @@ function flattenObject(src, prefix = "", dst = {}) {
|
|||||||
let v = src[k];
|
let v = src[k];
|
||||||
if (typeof v === "object" && !Array.isArray(v))
|
if (typeof v === "object" && !Array.isArray(v))
|
||||||
flattenObject(v, `${prefix}${k}.`, dst);
|
flattenObject(v, `${prefix}${k}.`, dst);
|
||||||
else
|
else dst[`${prefix}${k}`] = v;
|
||||||
dst[`${prefix}${k}`] = v;
|
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh(callback) {
|
function refresh(callback) {
|
||||||
if (!nextRefresh)
|
if (!nextRefresh) {
|
||||||
return setTimeout(function() {refresh(callback);}, 20);
|
return setTimeout(() => {
|
||||||
|
refresh(callback);
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
nextRefresh = 0;
|
nextRefresh = 0;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
cache.get("presets_hash", function(err, res) {
|
cache.get("presets_hash", (err, dbHash) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (hash && res === hash) {
|
if (hash && dbHash === hash) {
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.lock("presets_hash_lock", 3, function(err, unlockOrExtend) {
|
cache.lock("presets_hash_lock", 3, (err, unlockOrExtend) => {
|
||||||
if (err)
|
if (err) return callback(err);
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
let counter = 3;
|
let counter = 3;
|
||||||
|
|
||||||
counter += 2;
|
counter += 2;
|
||||||
db.getPresets(function(err, res) {
|
db.getPresets((err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (counter & 1)
|
if (counter & 1) callback(err);
|
||||||
callback(err);
|
return (counter = 0);
|
||||||
return counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getObjects(function(err, objects) {
|
db.getObjects((err, objects) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (counter & 1)
|
if (counter & 1) callback(err);
|
||||||
callback(err);
|
return (counter = 0);
|
||||||
return counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objects = objects.map(obj => {
|
objects = objects.map(obj => {
|
||||||
@@ -118,14 +113,12 @@ function refresh(callback) {
|
|||||||
if (!obj["_keys"] || !obj["_keys"].length)
|
if (!obj["_keys"] || !obj["_keys"].length)
|
||||||
obj["_keys"] = Object.keys(obj).filter(k => !k.startsWith("_"));
|
obj["_keys"] = Object.keys(obj).filter(k => !k.startsWith("_"));
|
||||||
|
|
||||||
return obj
|
return obj;
|
||||||
})
|
});
|
||||||
|
|
||||||
res.sort((a, b) => {
|
res.sort((a, b) => {
|
||||||
if (a.weight === b.weight)
|
if (a.weight === b.weight) return a._id > b._id;
|
||||||
return a._id > b._id;
|
else return a.weight - b.weight;
|
||||||
else
|
|
||||||
return a.weight - b.weight;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
presets = [];
|
presets = [];
|
||||||
@@ -134,14 +127,16 @@ function refresh(callback) {
|
|||||||
if (preset.schedule) {
|
if (preset.schedule) {
|
||||||
let parts = preset.schedule.trim().split(/\s+/);
|
let parts = preset.schedule.trim().split(/\s+/);
|
||||||
schedule = {
|
schedule = {
|
||||||
md5: crypto.createHash("md5").update(preset.schedule).digest("hex")
|
md5: crypto
|
||||||
|
.createHash("md5")
|
||||||
|
.update(preset.schedule)
|
||||||
|
.digest("hex")
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
schedule.duration = +(parts.shift()) * 1000;
|
schedule.duration = +parts.shift() * 1000;
|
||||||
schedule.schedule = scheduling.parseCron(parts.join(" "));
|
schedule.schedule = scheduling.parseCron(parts.join(" "));
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
logger.warn({
|
logger.warn({
|
||||||
message: "Invalid preset schedule",
|
message: "Invalid preset schedule",
|
||||||
preset: preset._id,
|
preset: preset._id,
|
||||||
@@ -152,7 +147,9 @@ function refresh(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let events = preset.events || {};
|
let events = preset.events || {};
|
||||||
let precondition = query.convertMongoQueryToFilters(JSON.parse(preset.precondition));
|
let precondition = query.convertMongoQueryToFilters(
|
||||||
|
JSON.parse(preset.precondition)
|
||||||
|
);
|
||||||
let _provisions = preset.provisions || [];
|
let _provisions = preset.provisions || [];
|
||||||
|
|
||||||
// Generate provisions from the old configuration format
|
// Generate provisions from the old configuration format
|
||||||
@@ -181,32 +178,41 @@ function refresh(callback) {
|
|||||||
case "add_object":
|
case "add_object":
|
||||||
for (let obj of objects) {
|
for (let obj of objects) {
|
||||||
if (obj["_id"] === c.object) {
|
if (obj["_id"] === c.object) {
|
||||||
let alias = obj["_keys"].map((k) => `${k}:${JSON.stringify(obj[k])}`).join(",");
|
let alias = obj["_keys"]
|
||||||
|
.map(k => `${k}:${JSON.stringify(obj[k])}`)
|
||||||
|
.join(",");
|
||||||
let p = `${c.name}.[${alias}]`;
|
let p = `${c.name}.[${alias}]`;
|
||||||
_provisions.push(["instances", p, 1]);
|
_provisions.push(["instances", p, 1]);
|
||||||
|
|
||||||
for (let k in obj)
|
for (let k in obj) {
|
||||||
if (!k.startsWith("_") && !(obj["_keys"].indexOf(k) !== -1))
|
if (
|
||||||
|
!k.startsWith("_") &&
|
||||||
|
!(obj["_keys"].indexOf(k) !== -1)
|
||||||
|
)
|
||||||
_provisions.push(["value", `${p}.${k}`, obj[k]]);
|
_provisions.push(["value", `${p}.${k}`, obj[k]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "delete_object":
|
case "delete_object":
|
||||||
for (let obj of objects) {
|
for (let obj of objects) {
|
||||||
if (obj["_id"] === c.object) {
|
if (obj["_id"] === c.object) {
|
||||||
let alias = obj["_keys"].map((k) => `${k}:${JSON.stringify(obj[k])}`).join(",");
|
let alias = obj["_keys"]
|
||||||
|
.map(k => `${k}:${JSON.stringify(obj[k])}`)
|
||||||
|
.join(",");
|
||||||
let p = `${c.name}.[${alias}]`;
|
let p = `${c.name}.[${alias}]`;
|
||||||
_provisions.push(["instances", p, 0]);
|
_provisions.push(["instances", p, 0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (counter & 1)
|
if (counter & 1)
|
||||||
callback(new Error(`Unknown configuration type ${c.type}`));
|
callback(new Error(`Unknown configuration type ${c.type}`));
|
||||||
return counter = 0;
|
return (counter = 0);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +228,7 @@ function refresh(callback) {
|
|||||||
|
|
||||||
if ((counter -= 2) === 1) {
|
if ((counter -= 2) === 1) {
|
||||||
computeHash();
|
computeHash();
|
||||||
cache.set("presets_hash", hash, 300, function(err) {
|
cache.set("presets_hash", hash, 300, err => {
|
||||||
unlockOrExtend(0);
|
unlockOrExtend(0);
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -232,23 +238,28 @@ function refresh(callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
counter += 2;
|
counter += 2;
|
||||||
db.getProvisions(function(err, res) {
|
db.getProvisions((err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (counter & 1)
|
if (counter & 1) callback(err);
|
||||||
callback(err);
|
return (counter = 0);
|
||||||
return counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provisions = {};
|
provisions = {};
|
||||||
for (let r of res) {
|
for (let r of res) {
|
||||||
provisions[r._id] = {};
|
provisions[r._id] = {};
|
||||||
provisions[r._id].md5 = crypto.createHash("md5").update(r.script).digest("hex");
|
provisions[r._id].md5 = crypto
|
||||||
provisions[r._id].script = new vm.Script(`"use strict";(function(){\n${r.script}\n})();`, {filename: r._id, lineOffset: -1, timeout: 50});
|
.createHash("md5")
|
||||||
|
.update(r.script)
|
||||||
|
.digest("hex");
|
||||||
|
provisions[r._id].script = new vm.Script(
|
||||||
|
`"use strict";(function(){\n${r.script}\n})();`,
|
||||||
|
{ filename: r._id, lineOffset: -1, timeout: 50 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((counter -= 2) === 1) {
|
if ((counter -= 2) === 1) {
|
||||||
computeHash();
|
computeHash();
|
||||||
cache.set("presets_hash", hash, 300, function(err) {
|
cache.set("presets_hash", hash, 300, err => {
|
||||||
unlockOrExtend(0);
|
unlockOrExtend(0);
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -257,23 +268,28 @@ function refresh(callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
counter += 2;
|
counter += 2;
|
||||||
db.getVirtualParameters(function(err, res) {
|
db.getVirtualParameters((err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (counter & 1)
|
if (counter & 1) callback(err);
|
||||||
callback(err);
|
return (counter = 0);
|
||||||
return counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtualParameters = {};
|
virtualParameters = {};
|
||||||
for (let r of res) {
|
for (let r of res) {
|
||||||
virtualParameters[r._id] = {}
|
virtualParameters[r._id] = {};
|
||||||
virtualParameters[r._id].md5 = crypto.createHash("md5").update(r.script).digest("hex");
|
virtualParameters[r._id].md5 = crypto
|
||||||
virtualParameters[r._id].script = new vm.Script(`"use strict";(function(){\n${r.script}\n})();`, {filename: r._id, lineOffset: -1, timeout: 50});
|
.createHash("md5")
|
||||||
|
.update(r.script)
|
||||||
|
.digest("hex");
|
||||||
|
virtualParameters[r._id].script = new vm.Script(
|
||||||
|
`"use strict";(function(){\n${r.script}\n})();`,
|
||||||
|
{ filename: r._id, lineOffset: -1, timeout: 50 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((counter -= 2) === 1) {
|
if ((counter -= 2) === 1) {
|
||||||
computeHash();
|
computeHash();
|
||||||
cache.set("presets_hash", hash, 300, function(err) {
|
cache.set("presets_hash", hash, 300, err => {
|
||||||
unlockOrExtend(0);
|
unlockOrExtend(0);
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -282,11 +298,10 @@ function refresh(callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
counter += 2;
|
counter += 2;
|
||||||
db.getFiles(function(err, res) {
|
db.getFiles((err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (counter & 1)
|
if (counter & 1) callback(err);
|
||||||
callback(err);
|
return (counter = 0);
|
||||||
return counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files = {};
|
files = {};
|
||||||
@@ -300,7 +315,7 @@ function refresh(callback) {
|
|||||||
|
|
||||||
if ((counter -= 2) === 1) {
|
if ((counter -= 2) === 1) {
|
||||||
computeHash();
|
computeHash();
|
||||||
cache.set("presets_hash", hash, 300, function(err) {
|
cache.set("presets_hash", hash, 300, err => {
|
||||||
unlockOrExtend(0);
|
unlockOrExtend(0);
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -310,7 +325,7 @@ function refresh(callback) {
|
|||||||
|
|
||||||
if ((counter -= 2) === 1) {
|
if ((counter -= 2) === 1) {
|
||||||
computeHash();
|
computeHash();
|
||||||
cache.set("presets_hash", hash, 300, function(err) {
|
cache.set("presets_hash", hash, 300, err => {
|
||||||
unlockOrExtend(0);
|
unlockOrExtend(0);
|
||||||
nextRefresh = now + (REFRESH - (now % REFRESH));
|
nextRefresh = now + (REFRESH - (now % REFRESH));
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -320,37 +335,31 @@ function refresh(callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getPresets(callback) {
|
function getPresets(callback) {
|
||||||
if (Date.now() < nextRefresh)
|
if (Date.now() < nextRefresh) return callback(null, hash, presets);
|
||||||
return callback(null, hash, presets);
|
|
||||||
|
|
||||||
refresh(function(err) {
|
refresh(err => {
|
||||||
callback(err, hash, presets);
|
callback(err, hash, presets);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getFiles(callback) {
|
function getFiles(callback) {
|
||||||
if (Date.now() < nextRefresh)
|
if (Date.now() < nextRefresh) return callback(null, hash, files);
|
||||||
return callback(null, hash, files);
|
|
||||||
|
|
||||||
refresh(function(err) {
|
refresh(err => {
|
||||||
callback(err, hash, files);
|
callback(err, hash, files);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getProvisionsAndVirtualParameters(callback) {
|
function getProvisionsAndVirtualParameters(callback) {
|
||||||
if (Date.now() < nextRefresh)
|
if (Date.now() < nextRefresh)
|
||||||
return callback(null, hash, provisions, virtualParameters);
|
return callback(null, hash, provisions, virtualParameters);
|
||||||
|
|
||||||
refresh(function(err) {
|
refresh(err => {
|
||||||
callback(err, hash, provisions, virtualParameters);
|
callback(err, hash, provisions, virtualParameters);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.getPresets = getPresets;
|
exports.getPresets = getPresets;
|
||||||
exports.getFiles = getFiles;
|
exports.getFiles = getFiles;
|
||||||
exports.getProvisionsAndVirtualParameters = getProvisionsAndVirtualParameters;
|
exports.getProvisionsAndVirtualParameters = getProvisionsAndVirtualParameters;
|
||||||
+50
-68
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -35,57 +35,61 @@ let ACCESS_LOG_SYSTEMD = false;
|
|||||||
|
|
||||||
let LOG_FILE, ACCESS_LOG_FILE;
|
let LOG_FILE, ACCESS_LOG_FILE;
|
||||||
|
|
||||||
let logStream = fs.createWriteStream(null, {fd: process.stderr.fd});
|
let logStream = fs.createWriteStream(null, { fd: process.stderr.fd });
|
||||||
let logStat = fs.fstatSync(logStream.fd);
|
let logStat = fs.fstatSync(logStream.fd);
|
||||||
let accessLogStream = fs.createWriteStream(null, {fd: process.stdout.fd});
|
let accessLogStream = fs.createWriteStream(null, { fd: process.stdout.fd });
|
||||||
let accessLogStat = fs.fstatSync(accessLogStream.fd);
|
let accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||||
|
|
||||||
|
|
||||||
// Reopen if original files have been moved (e.g. logrotate)
|
// Reopen if original files have been moved (e.g. logrotate)
|
||||||
function reopen() {
|
function reopen() {
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
|
|
||||||
if (LOG_FILE) {
|
if (LOG_FILE) {
|
||||||
++ counter;
|
++counter;
|
||||||
fs.stat(LOG_FILE, function(err, stat) {
|
fs.stat(LOG_FILE, (err, stat) => {
|
||||||
if (err && !err.message.startsWith("ENOENT:"))
|
if (err && !err.message.startsWith("ENOENT:")) throw err;
|
||||||
throw err;
|
|
||||||
|
|
||||||
if (!(stat && stat.dev === logStat.dev && stat.ino === logStat.ino)) {
|
if (!(stat && stat.dev === logStat.dev && stat.ino === logStat.ino)) {
|
||||||
logStream.end();
|
logStream.end();
|
||||||
logStream = fs.createWriteStream(null,
|
logStream = fs.createWriteStream(null, {
|
||||||
{fd: fs.openSync(LOG_FILE, "a")});
|
fd: fs.openSync(LOG_FILE, "a")
|
||||||
|
});
|
||||||
logStat = fs.fstatSync(logStream.fd);
|
logStat = fs.fstatSync(logStream.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-- counter === 0)
|
if (--counter === 0)
|
||||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ACCESS_LOG_FILE) {
|
if (ACCESS_LOG_FILE) {
|
||||||
++ counter;
|
++counter;
|
||||||
fs.stat(ACCESS_LOG_FILE, function(err, stat) {
|
fs.stat(ACCESS_LOG_FILE, (err, stat) => {
|
||||||
if (err && !err.message.startsWith("ENOENT:"))
|
if (err && !err.message.startsWith("ENOENT:")) throw err;
|
||||||
throw err;
|
|
||||||
|
|
||||||
if (!(stat && stat.dev === accessLogStat.dev && stat.ino === accessLogStat.ino)) {
|
if (
|
||||||
|
!(
|
||||||
|
stat &&
|
||||||
|
stat.dev === accessLogStat.dev &&
|
||||||
|
stat.ino === accessLogStat.ino
|
||||||
|
)
|
||||||
|
) {
|
||||||
accessLogStream.end();
|
accessLogStream.end();
|
||||||
accessLogStream = fs.createWriteStream(null,
|
accessLogStream = fs.createWriteStream(null, {
|
||||||
{fd: fs.openSync(ACCESS_LOG_FILE, "a")});
|
fd: fs.openSync(ACCESS_LOG_FILE, "a")
|
||||||
|
});
|
||||||
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-- counter === 0)
|
if (--counter === 0)
|
||||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-- counter === 0)
|
if (--counter === 0)
|
||||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function init(service, version) {
|
function init(service, version) {
|
||||||
defaultMeta.hostname = os.hostname();
|
defaultMeta.hostname = os.hostname();
|
||||||
defaultMeta.pid = process.pid;
|
defaultMeta.pid = process.pid;
|
||||||
@@ -96,14 +100,14 @@ function init(service, version) {
|
|||||||
ACCESS_LOG_FILE = config.get(`${service.toUpperCase()}_ACCESS_LOG_FILE`);
|
ACCESS_LOG_FILE = config.get(`${service.toUpperCase()}_ACCESS_LOG_FILE`);
|
||||||
|
|
||||||
if (LOG_FILE) {
|
if (LOG_FILE) {
|
||||||
logStream = fs.createWriteStream(null,
|
logStream = fs.createWriteStream(null, { fd: fs.openSync(LOG_FILE, "a") });
|
||||||
{fd: fs.openSync(LOG_FILE, "a")});
|
|
||||||
logStat = fs.fstatSync(logStream.fd);
|
logStat = fs.fstatSync(logStream.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ACCESS_LOG_FILE) {
|
if (ACCESS_LOG_FILE) {
|
||||||
accessLogStream = fs.createWriteStream(null,
|
accessLogStream = fs.createWriteStream(null, {
|
||||||
{fd: fs.openSync(ACCESS_LOG_FILE, "a")});
|
fd: fs.openSync(ACCESS_LOG_FILE, "a")
|
||||||
|
});
|
||||||
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,27 +119,26 @@ function init(service, version) {
|
|||||||
dev = parseInt(dev);
|
dev = parseInt(dev);
|
||||||
inode = parseInt(inode);
|
inode = parseInt(inode);
|
||||||
|
|
||||||
LOG_SYSTEMD = (logStat.dev === dev && logStat.ino === inode);
|
LOG_SYSTEMD = logStat.dev === dev && logStat.ino === inode;
|
||||||
ACCESS_LOG_SYSTEMD = (accessLogStat.dev === dev && accessLogStat.ino === inode);
|
ACCESS_LOG_SYSTEMD =
|
||||||
|
accessLogStat.dev === dev && accessLogStat.ino === inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG_FILE || ACCESS_LOG_FILE) {
|
if (LOG_FILE || ACCESS_LOG_FILE)
|
||||||
// Can't use setInterval as we need all workers to cehck at the same time
|
// Can't use setInterval as we need all workers to cehck at the same time
|
||||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
accessLogStream.end();
|
accessLogStream.end();
|
||||||
logStream.end();
|
logStream.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function flatten(details) {
|
function flatten(details) {
|
||||||
if (details.sessionContext) {
|
if (details.sessionContext) {
|
||||||
details.deviceId = details.sessionContext.deviceId;
|
details.deviceId = details.sessionContext.deviceId;
|
||||||
details.remoteAddress = details.sessionContext.httpRequest.connection.remoteAddress;
|
details.remoteAddress =
|
||||||
|
details.sessionContext.httpRequest.connection.remoteAddress;
|
||||||
delete details.sessionContext;
|
delete details.sessionContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,20 +160,17 @@ function flatten(details) {
|
|||||||
details.acsRequestName = details.rpc.acsRequest.name;
|
details.acsRequestName = details.rpc.acsRequest.name;
|
||||||
if (details.rpc.acsRequest.commandKey)
|
if (details.rpc.acsRequest.commandKey)
|
||||||
details.acsRequestCommandKey = details.rpc.acsRequest.commandKey;
|
details.acsRequestCommandKey = details.rpc.acsRequest.commandKey;
|
||||||
}
|
} else if (details.rpc.cpeRequest) {
|
||||||
else if (details.rpc.cpeRequest) {
|
|
||||||
details.cpeRequestId = details.rpc.id;
|
details.cpeRequestId = details.rpc.id;
|
||||||
if (details.rpc.cpeRequest.name === "Inform") {
|
if (details.rpc.cpeRequest.name === "Inform") {
|
||||||
details.informEvent = details.rpc.cpeRequest.event.join(",");
|
details.informEvent = details.rpc.cpeRequest.event.join(",");
|
||||||
details.informRetryCount = details.rpc.cpeRequest.retryCount;
|
details.informRetryCount = details.rpc.cpeRequest.retryCount;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
details.cpeRequestName = details.rpc.cpeRequest.name;
|
details.cpeRequestName = details.rpc.cpeRequest.name;
|
||||||
if (details.rpc.cpeRequest.commandKey)
|
if (details.rpc.cpeRequest.commandKey)
|
||||||
details.cpeRequestCommandKey = details.rpc.cpeRequest.commandKey;
|
details.cpeRequestCommandKey = details.rpc.cpeRequest.commandKey;
|
||||||
}
|
}
|
||||||
}
|
} else if (details.rpc.cpeFault) {
|
||||||
else if (details.rpc.cpeFault) {
|
|
||||||
details.acsRequestId = details.rpc.id;
|
details.acsRequestId = details.rpc.id;
|
||||||
details.cpeFaultCode = details.rpc.cpeFault.detail.faultCode;
|
details.cpeFaultCode = details.rpc.cpeFault.detail.faultCode;
|
||||||
details.cpeFaultString = details.rpc.cpeFault.detail.faultString;
|
details.cpeFaultString = details.rpc.cpeFault.detail.faultString;
|
||||||
@@ -187,16 +187,12 @@ function flatten(details) {
|
|||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatJson(details, systemd) {
|
function formatJson(details, systemd) {
|
||||||
if (systemd) {
|
if (systemd) {
|
||||||
let severity = "";
|
let severity = "";
|
||||||
if (details.severity === "info")
|
if (details.severity === "info") severity = "<6>";
|
||||||
severity = "<6>";
|
else if (details.severity === "warn") severity = "<4>";
|
||||||
else if (details.severity === "warn")
|
else if (details.severity === "error") severity = "<3>";
|
||||||
severity = "<4>";
|
|
||||||
else if (details.severity === "error")
|
|
||||||
severity = "<3>";
|
|
||||||
|
|
||||||
return `${severity}${JSON.stringify(flatten(details))}${os.EOL}`;
|
return `${severity}${JSON.stringify(flatten(details))}${os.EOL}`;
|
||||||
}
|
}
|
||||||
@@ -204,7 +200,6 @@ function formatJson(details, systemd) {
|
|||||||
return `${JSON.stringify(flatten(details))}${os.EOL}`;
|
return `${JSON.stringify(flatten(details))}${os.EOL}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatSimple(details, systemd) {
|
function formatSimple(details, systemd) {
|
||||||
flatten(details);
|
flatten(details);
|
||||||
|
|
||||||
@@ -212,8 +207,7 @@ function formatSimple(details, systemd) {
|
|||||||
if (details.remoteAddress) {
|
if (details.remoteAddress) {
|
||||||
if (details.deviceId)
|
if (details.deviceId)
|
||||||
remote = `${details.remoteAddress} ${details.deviceId}: `;
|
remote = `${details.remoteAddress} ${details.deviceId}: `;
|
||||||
else
|
else remote = `${details.remoteAddress}: `;
|
||||||
remote = `${details.remoteAddress}: `;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(details);
|
const keys = Object.keys(details);
|
||||||
@@ -229,28 +223,24 @@ function formatSimple(details, systemd) {
|
|||||||
|
|
||||||
let kv = [];
|
let kv = [];
|
||||||
for (let k of keys)
|
for (let k of keys)
|
||||||
if (!skip[k])
|
if (!skip[k]) kv.push(`${k}=${JSON.stringify(details[k])}`);
|
||||||
kv.push(`${k}=${JSON.stringify(details[k])}`);
|
|
||||||
|
|
||||||
if (kv.length)
|
if (kv.length) meta = `; ${kv.join(" ")}`;
|
||||||
meta = `; ${kv.join(" ")}`;
|
|
||||||
|
|
||||||
if (systemd) {
|
if (systemd) {
|
||||||
let severity = "";
|
let severity = "";
|
||||||
if (details.severity === "info")
|
if (details.severity === "info") severity = "<6>";
|
||||||
severity = "<6>";
|
else if (details.severity === "warn") severity = "<4>";
|
||||||
else if (details.severity === "warn")
|
else if (details.severity === "error") severity = "<3>";
|
||||||
severity = "<4>";
|
|
||||||
else if (details.severity === "error")
|
|
||||||
severity = "<3>";
|
|
||||||
|
|
||||||
return `${severity}${remote}${details.message}${meta}${os.EOL}`;
|
return `${severity}${remote}${details.message}${meta}${os.EOL}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${details.timestamp} [${details.severity.toUpperCase()}] ${remote}${details.message}${meta}${os.EOL}`;
|
return `${details.timestamp} [${details.severity.toUpperCase()}] ${remote}${
|
||||||
|
details.message
|
||||||
|
}${meta}${os.EOL}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function log(details) {
|
function log(details) {
|
||||||
details.timestamp = new Date().toISOString();
|
details.timestamp = new Date().toISOString();
|
||||||
if (LOG_FORMAT === "json") {
|
if (LOG_FORMAT === "json") {
|
||||||
@@ -261,25 +251,21 @@ function log(details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function info(details) {
|
function info(details) {
|
||||||
details.severity = "info";
|
details.severity = "info";
|
||||||
log(details);
|
log(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function warn(details) {
|
function warn(details) {
|
||||||
details.severity = "warn";
|
details.severity = "warn";
|
||||||
log(details);
|
log(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function error(details) {
|
function error(details) {
|
||||||
details.severity = "error";
|
details.severity = "error";
|
||||||
log(details);
|
log(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function accessLog(details) {
|
function accessLog(details) {
|
||||||
details.timestamp = new Date().toISOString();
|
details.timestamp = new Date().toISOString();
|
||||||
if (ACCESS_LOG_FORMAT === "json") {
|
if (ACCESS_LOG_FORMAT === "json") {
|
||||||
@@ -290,25 +276,21 @@ function accessLog(details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function accessInfo(details) {
|
function accessInfo(details) {
|
||||||
details.severity = "info";
|
details.severity = "info";
|
||||||
accessLog(details);
|
accessLog(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function accessWarn(details) {
|
function accessWarn(details) {
|
||||||
details.severity = "warn";
|
details.severity = "warn";
|
||||||
accessLog(details);
|
accessLog(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function accessError(details) {
|
function accessError(details) {
|
||||||
details.severity = "error";
|
details.severity = "error";
|
||||||
accessLog(details);
|
accessLog(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.init = init;
|
exports.init = init;
|
||||||
exports.close = close;
|
exports.close = close;
|
||||||
exports.flatten = flatten;
|
exports.flatten = flatten;
|
||||||
|
|||||||
+226
-216
@@ -31,22 +31,22 @@ const cache = require("./cache");
|
|||||||
|
|
||||||
const VERSION = require("../package.json").version;
|
const VERSION = require("../package.json").version;
|
||||||
|
|
||||||
const DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/?$/;
|
const DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/tasks\/?$/;
|
||||||
const TASKS_REGEX = /^\/tasks\/([a-zA-Z0-9\-\_\%]+)(\/[a-zA-Z_]*)?$/;
|
const TASKS_REGEX = /^\/tasks\/([a-zA-Z0-9\-_%]+)(\/[a-zA-Z_]*)?$/;
|
||||||
const TAGS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tags\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const TAGS_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/tags\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const PRESETS_REGEX = /^\/presets\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const PRESETS_REGEX = /^\/presets\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const OBJECTS_REGEX = /^\/objects\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const OBJECTS_REGEX = /^\/objects\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const FILES_REGEX = /^\/files\/([a-zA-Z0-9\%\!\*\'\(\)\;\:\@\&\=\+\$\,\?\#\[\]\-\_\.\~]+)\/?$/;
|
const FILES_REGEX = /^\/files\/([a-zA-Z0-9%!*'();:@&=+$,?#[\]\-_.~]+)\/?$/;
|
||||||
const PING_REGEX = /^\/ping\/([a-zA-Z0-9\-\_\.\:]+)\/?$/;
|
const PING_REGEX = /^\/ping\/([a-zA-Z0-9\-_.:]+)\/?$/;
|
||||||
const QUERY_REGEX = /^\/([a-zA-Z0-9_]+)\/?$/;
|
const QUERY_REGEX = /^\/([a-zA-Z0-9_]+)\/?$/;
|
||||||
const DELETE_DEVICE_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const DELETE_DEVICE_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const PROVISIONS_REGEX = /^\/provisions\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const PROVISIONS_REGEX = /^\/provisions\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const VIRTUAL_PARAMETERS_REGEX = /^\/virtual_parameters\/([a-zA-Z0-9\-\_\%]+)\/?$/;
|
const VIRTUAL_PARAMETERS_REGEX = /^\/virtual_parameters\/([a-zA-Z0-9\-_%]+)\/?$/;
|
||||||
const FAULTS_REGEX = /^\/faults\/([a-zA-Z0-9\-\_\%\:]+)\/?$/;
|
const FAULTS_REGEX = /^\/faults\/([a-zA-Z0-9\-_%:]+)\/?$/;
|
||||||
|
|
||||||
function throwError(err, httpResponse) {
|
function throwError(err, httpResponse) {
|
||||||
if (httpResponse) {
|
if (httpResponse) {
|
||||||
httpResponse.writeHead(500, {"Connection": "close"});
|
httpResponse.writeHead(500, { Connection: "close" });
|
||||||
httpResponse.end(`${err.name}: ${err.message}`);
|
httpResponse.end(`${err.name}: ${err.message}`);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@@ -57,7 +57,7 @@ function listener(request, response) {
|
|||||||
let bytes = 0;
|
let bytes = 0;
|
||||||
response.setHeader("GenieACS-Version", VERSION);
|
response.setHeader("GenieACS-Version", VERSION);
|
||||||
|
|
||||||
request.addListener("data", (chunk) => {
|
request.addListener("data", chunk => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
bytes += chunk.length;
|
bytes += chunk.length;
|
||||||
});
|
});
|
||||||
@@ -77,75 +77,73 @@ function listener(request, response) {
|
|||||||
const body = getBody();
|
const body = getBody();
|
||||||
const urlParts = url.parse(request.url, true);
|
const urlParts = url.parse(request.url, true);
|
||||||
if (PRESETS_REGEX.test(urlParts.pathname)) {
|
if (PRESETS_REGEX.test(urlParts.pathname)) {
|
||||||
const presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1]);
|
const presetName = querystring.unescape(
|
||||||
|
PRESETS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const preset = JSON.parse(body);
|
const preset = JSON.parse(body);
|
||||||
preset._id = presetName;
|
preset._id = presetName;
|
||||||
db.presetsCollection.save(preset, (err) => {
|
db.presetsCollection.save(preset, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
db.presetsCollection.remove({"_id": presetName}, (err) => {
|
db.presetsCollection.remove({ _id: presetName }, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT, DELETE"});
|
response.writeHead(405, { Allow: "PUT, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (OBJECTS_REGEX.test(urlParts.pathname)) {
|
} else if (OBJECTS_REGEX.test(urlParts.pathname)) {
|
||||||
const objectName = querystring.unescape(OBJECTS_REGEX.exec(urlParts.pathname)[1]);
|
const objectName = querystring.unescape(
|
||||||
|
OBJECTS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const object = JSON.parse(body);
|
const object = JSON.parse(body);
|
||||||
object._id = objectName;
|
object._id = objectName;
|
||||||
db.objectsCollection.save(object, (err) => {
|
db.objectsCollection.save(object, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
db.objectsCollection.remove({"_id": objectName}, (err) => {
|
db.objectsCollection.remove({ _id: objectName }, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT, DELETE"});
|
response.writeHead(405, { Allow: "PUT, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (PROVISIONS_REGEX.test(urlParts.pathname)) {
|
} else if (PROVISIONS_REGEX.test(urlParts.pathname)) {
|
||||||
const provisionName = querystring.unescape(PROVISIONS_REGEX.exec(urlParts.pathname)[1]);
|
const provisionName = querystring.unescape(
|
||||||
|
PROVISIONS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const object = {
|
const object = {
|
||||||
_id: provisionName,
|
_id: provisionName,
|
||||||
@@ -160,37 +158,35 @@ function listener(request, response) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.provisionsCollection.save(object, (err) => {
|
db.provisionsCollection.save(object, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
db.provisionsCollection.remove({"_id": provisionName}, (err) => {
|
db.provisionsCollection.remove({ _id: provisionName }, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT, DELETE"});
|
response.writeHead(405, { Allow: "PUT, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (VIRTUAL_PARAMETERS_REGEX.test(urlParts.pathname)) {
|
} else if (VIRTUAL_PARAMETERS_REGEX.test(urlParts.pathname)) {
|
||||||
const virtualParameterName = querystring.unescape(VIRTUAL_PARAMETERS_REGEX.exec(urlParts.pathname)[1]);
|
const virtualParameterName = querystring.unescape(
|
||||||
|
VIRTUAL_PARAMETERS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const object = {
|
const object = {
|
||||||
_id: virtualParameterName,
|
_id: virtualParameterName,
|
||||||
@@ -205,33 +201,32 @@ function listener(request, response) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.virtualParametersCollection.save(object, (err) => {
|
db.virtualParametersCollection.save(object, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
db.virtualParametersCollection.remove({"_id": virtualParameterName}, (err) => {
|
db.virtualParametersCollection.remove(
|
||||||
if (err)
|
{ _id: virtualParameterName },
|
||||||
return throwError(err, response);
|
err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
cache.del("presets_hash", (err) => {
|
cache.del("presets_hash", err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT, DELETE"});
|
response.writeHead(405, { Allow: "PUT, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (TAGS_REGEX.test(urlParts.pathname)) {
|
} else if (TAGS_REGEX.test(urlParts.pathname)) {
|
||||||
@@ -240,51 +235,48 @@ function listener(request, response) {
|
|||||||
const tag = querystring.unescape(r[2]);
|
const tag = querystring.unescape(r[2]);
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
db.devicesCollection.update(
|
db.devicesCollection.update(
|
||||||
{"_id": deviceId},
|
{ _id: deviceId },
|
||||||
{"$addToSet": {"_tags": tag}},
|
{ $addToSet: { _tags: tag } },
|
||||||
{safe: true},
|
{ safe: true },
|
||||||
(err) => {
|
err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
db.devicesCollection.update(
|
db.devicesCollection.update(
|
||||||
{"_id": deviceId},
|
{ _id: deviceId },
|
||||||
{"$pull": {"_tags": tag}},
|
{ $pull: { _tags: tag } },
|
||||||
{safe: true},
|
{ safe: true },
|
||||||
(err) => {
|
err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "POST, DELETE"});
|
response.writeHead(405, { Allow: "POST, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (FAULTS_REGEX.test(urlParts.pathname)) {
|
} else if (FAULTS_REGEX.test(urlParts.pathname)) {
|
||||||
if (request.method === "DELETE") {
|
if (request.method === "DELETE") {
|
||||||
const faultId = querystring.unescape(FAULTS_REGEX.exec(urlParts.pathname)[1]);
|
const faultId = querystring.unescape(
|
||||||
|
FAULTS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
const deviceId = faultId.split(":", 1)[0];
|
const deviceId = faultId.split(":", 1)[0];
|
||||||
const channel = faultId.slice(deviceId.length + 1);
|
const channel = faultId.slice(deviceId.length + 1);
|
||||||
db.faultsCollection.remove({_id: faultId}, (err) => {
|
db.faultsCollection.remove({ _id: faultId }, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
if (channel.startsWith("task_")) {
|
if (channel.startsWith("task_")) {
|
||||||
const objId = new mongodb.ObjectID(channel.slice(5));
|
const objId = new mongodb.ObjectID(channel.slice(5));
|
||||||
return db.tasksCollection.remove({_id: objId}, (err) => {
|
return db.tasksCollection.remove({ _id: objId }, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, (err) => {
|
cache.del(`${deviceId}_tasks_faults_operations`, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
@@ -292,67 +284,81 @@ function listener(request, response) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, (err) => {
|
cache.del(`${deviceId}_tasks_faults_operations`, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "DELETE"});
|
response.writeHead(405, { Allow: "DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (DEVICE_TASKS_REGEX.test(urlParts.pathname)) {
|
} else if (DEVICE_TASKS_REGEX.test(urlParts.pathname)) {
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
const deviceId = querystring.unescape(DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1]);
|
const deviceId = querystring.unescape(
|
||||||
|
DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (body.length) {
|
if (body.length) {
|
||||||
const task = JSON.parse(body);
|
const task = JSON.parse(body);
|
||||||
task.device = deviceId;
|
task.device = deviceId;
|
||||||
apiFunctions.insertTasks(task, (err) => {
|
apiFunctions.insertTasks(task, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, (err) => {
|
cache.del(`${deviceId}_tasks_faults_operations`, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
if (urlParts.query.connection_request != null) {
|
if (urlParts.query.connection_request != null) {
|
||||||
apiFunctions.connectionRequest(deviceId, (err) => {
|
apiFunctions.connectionRequest(deviceId, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
response.writeHead(202, err.message, {"Content-Type": "application/json"});
|
response.writeHead(202, err.message, {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
});
|
||||||
response.end(JSON.stringify(task));
|
response.end(JSON.stringify(task));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEVICE_ONLINE_THRESHOLD = config.get("DEVICE_ONLINE_THRESHOLD", deviceId);
|
const DEVICE_ONLINE_THRESHOLD = config.get(
|
||||||
apiFunctions.watchTask(deviceId, task._id, DEVICE_ONLINE_THRESHOLD, (err, status) => {
|
"DEVICE_ONLINE_THRESHOLD",
|
||||||
if (err)
|
deviceId
|
||||||
return throwError(err, response);
|
);
|
||||||
|
apiFunctions.watchTask(
|
||||||
|
deviceId,
|
||||||
|
task._id,
|
||||||
|
DEVICE_ONLINE_THRESHOLD,
|
||||||
|
(err, status) => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
if (status === "timeout") {
|
if (status === "timeout") {
|
||||||
response.writeHead(202, "Task queued but not processed", {
|
response.writeHead(
|
||||||
"Content-Type": "application/json"
|
202,
|
||||||
});
|
"Task queued but not processed",
|
||||||
response.end(JSON.stringify(task));
|
{
|
||||||
} else if (status === "fault") {
|
"Content-Type": "application/json"
|
||||||
db.tasksCollection.findOne({_id: task._id}, (err, task) => {
|
}
|
||||||
if (err)
|
);
|
||||||
return throwError(err, response);
|
response.end(JSON.stringify(task));
|
||||||
|
} else if (status === "fault") {
|
||||||
|
db.tasksCollection.findOne(
|
||||||
|
{ _id: task._id },
|
||||||
|
(err, task2) => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
response.writeHead(202, "Task faulted", {
|
response.writeHead(202, "Task faulted", {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
});
|
||||||
|
response.end(JSON.stringify(task2));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
response.writeHead(200, {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
});
|
});
|
||||||
response.end(JSON.stringify(task));
|
response.end(JSON.stringify(task));
|
||||||
});
|
}
|
||||||
} else {
|
|
||||||
response.writeHead(200, {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
});
|
|
||||||
response.end(JSON.stringify(task));
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(202, {
|
response.writeHead(202, {
|
||||||
@@ -364,7 +370,7 @@ function listener(request, response) {
|
|||||||
});
|
});
|
||||||
} else if (urlParts.query.connection_request != null) {
|
} else if (urlParts.query.connection_request != null) {
|
||||||
// No task, send connection request only
|
// No task, send connection request only
|
||||||
apiFunctions.connectionRequest(deviceId, (err) => {
|
apiFunctions.connectionRequest(deviceId, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
response.writeHead(504);
|
response.writeHead(504);
|
||||||
response.end(`${err.name}: ${err.message}`);
|
response.end(`${err.name}: ${err.message}`);
|
||||||
@@ -378,7 +384,7 @@ function listener(request, response) {
|
|||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "POST"});
|
response.writeHead(405, { Allow: "POST" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (TASKS_REGEX.test(urlParts.pathname)) {
|
} else if (TASKS_REGEX.test(urlParts.pathname)) {
|
||||||
@@ -388,63 +394,70 @@ function listener(request, response) {
|
|||||||
if (!action || action === "/") {
|
if (!action || action === "/") {
|
||||||
if (request.method === "DELETE") {
|
if (request.method === "DELETE") {
|
||||||
db.tasksCollection.findOne(
|
db.tasksCollection.findOne(
|
||||||
{"_id": new mongodb.ObjectID(taskId)},
|
{ _id: new mongodb.ObjectID(taskId) },
|
||||||
{"device": 1}, (err, task) => {
|
{ device: 1 },
|
||||||
if (err)
|
(err, task) => {
|
||||||
return throwError(err, response);
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end("Task not found");
|
response.end("Task not found");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deviceId = task.device;
|
||||||
|
db.tasksCollection.remove(
|
||||||
|
{ _id: new mongodb.ObjectID(taskId) },
|
||||||
|
err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
|
db.faultsCollection.remove(
|
||||||
|
{ _id: `${deviceId}:task_${taskId}` },
|
||||||
|
err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
|
cache.del(`${deviceId}_tasks_faults_operations`, err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
|
response.writeHead(200);
|
||||||
|
response.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
const deviceId = task.device;
|
|
||||||
db.tasksCollection.remove(
|
|
||||||
{"_id": new mongodb.ObjectID(taskId)}, (err) => {
|
|
||||||
if (err)
|
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
db.faultsCollection.remove({_id: `${deviceId}:task_${taskId}`}, (err) => {
|
|
||||||
if (err)
|
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, (err) => {
|
|
||||||
if (err)
|
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT DELETE"});
|
response.writeHead(405, { Allow: "PUT DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (action === "/retry") {
|
} else if (action === "/retry") {
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
db.tasksCollection.findOne({"_id": new mongodb.ObjectID(taskId)}, {"device": 1}, (err, task) => {
|
db.tasksCollection.findOne(
|
||||||
if (err)
|
{ _id: new mongodb.ObjectID(taskId) },
|
||||||
return throwError(err, response);
|
{ device: 1 },
|
||||||
|
(err, task) => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
const deviceId = task.device;
|
const deviceId = task.device;
|
||||||
db.faultsCollection.remove({_id: `${deviceId}:task_${taskId}`}, (err) => {
|
db.faultsCollection.remove(
|
||||||
if (err)
|
{ _id: `${deviceId}:task_${taskId}` },
|
||||||
return throwError(err, response);
|
err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
cache.del(`${deviceId}_tasks_faults_operations`, (err) => {
|
cache.del(`${deviceId}_tasks_faults_operations`, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "POST"});
|
response.writeHead(405, { Allow: "POST" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -452,7 +465,9 @@ function listener(request, response) {
|
|||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
} else if (FILES_REGEX.test(urlParts.pathname)) {
|
} else if (FILES_REGEX.test(urlParts.pathname)) {
|
||||||
const filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1]);
|
const filename = querystring.unescape(
|
||||||
|
FILES_REGEX.exec(urlParts.pathname)[1]
|
||||||
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const metadata = {
|
const metadata = {
|
||||||
fileType: request.headers.filetype,
|
fileType: request.headers.filetype,
|
||||||
@@ -463,17 +478,14 @@ function listener(request, response) {
|
|||||||
const gs = new mongodb.GridStore(db.mongoDb, filename, filename, "w", {
|
const gs = new mongodb.GridStore(db.mongoDb, filename, filename, "w", {
|
||||||
metadata: metadata
|
metadata: metadata
|
||||||
});
|
});
|
||||||
gs.open((err, gs) => {
|
gs.open(err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
gs.write(body, (err) => {
|
gs.write(body, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
gs.close((err) => {
|
gs.close(err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(201);
|
response.writeHead(201);
|
||||||
response.end();
|
response.end();
|
||||||
@@ -481,22 +493,21 @@ function listener(request, response) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
mongodb.GridStore.unlink(db.mongoDb, filename, (err) => {
|
mongodb.GridStore.unlink(db.mongoDb, filename, err => {
|
||||||
if (err)
|
if (err) return throwError(err, response);
|
||||||
return throwError(err, response);
|
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405, {"Allow": "PUT, DELETE"});
|
response.writeHead(405, { Allow: "PUT, DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
} else if (PING_REGEX.test(urlParts.pathname)) {
|
} else if (PING_REGEX.test(urlParts.pathname)) {
|
||||||
const host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1]);
|
const host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1]);
|
||||||
childProcess.exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => {
|
childProcess.exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
response.writeHead(404, {"Cache-Control": "no-cache"});
|
response.writeHead(404, { "Cache-Control": "no-cache" });
|
||||||
response.end(`${err.name}: ${err.message}`);
|
response.end(`${err.name}: ${err.message}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -509,15 +520,16 @@ function listener(request, response) {
|
|||||||
});
|
});
|
||||||
} else if (DELETE_DEVICE_REGEX.test(urlParts.pathname)) {
|
} else if (DELETE_DEVICE_REGEX.test(urlParts.pathname)) {
|
||||||
if (request.method !== "DELETE") {
|
if (request.method !== "DELETE") {
|
||||||
response.writeHead(405, {"Allow": "DELETE"});
|
response.writeHead(405, { Allow: "DELETE" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceId = querystring.unescape(DELETE_DEVICE_REGEX.exec(urlParts.pathname)[1]);
|
const deviceId = querystring.unescape(
|
||||||
apiFunctions.deleteDevice(deviceId, (err) => {
|
DELETE_DEVICE_REGEX.exec(urlParts.pathname)[1]
|
||||||
if (err)
|
);
|
||||||
return throwError(err, response);
|
apiFunctions.deleteDevice(deviceId, err => {
|
||||||
|
if (err) return throwError(err, response);
|
||||||
|
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end();
|
response.end();
|
||||||
@@ -528,13 +540,15 @@ function listener(request, response) {
|
|||||||
// Convert to camel case
|
// Convert to camel case
|
||||||
let i = collectionName.indexOf("_");
|
let i = collectionName.indexOf("_");
|
||||||
while (i++ >= 0) {
|
while (i++ >= 0) {
|
||||||
const up = i < collectionName.length ? collectionName[i].toUpperCase() : "";
|
const up =
|
||||||
collectionName = collectionName.slice(0, i - 1) + up + collectionName.slice(i + 1);
|
i < collectionName.length ? collectionName[i].toUpperCase() : "";
|
||||||
|
collectionName =
|
||||||
|
collectionName.slice(0, i - 1) + up + collectionName.slice(i + 1);
|
||||||
i = collectionName.indexOf("_", i);
|
i = collectionName.indexOf("_", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.method !== "GET" && request.method !== "HEAD") {
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
||||||
response.writeHead(405, {"Allow": "GET, HEAD"});
|
response.writeHead(405, { Allow: "GET, HEAD" });
|
||||||
response.end("405 Method Not Allowed");
|
response.end("405 Method Not Allowed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -563,14 +577,14 @@ function listener(request, response) {
|
|||||||
break;
|
break;
|
||||||
case "tasks":
|
case "tasks":
|
||||||
q = query.sanitizeQueryTypes(q, {
|
q = query.sanitizeQueryTypes(q, {
|
||||||
_id: (v) => new mongodb.ObjectID(v),
|
_id: v => new mongodb.ObjectID(v),
|
||||||
timestamp: (v) => new Date(v),
|
timestamp: v => new Date(v),
|
||||||
retries: Number
|
retries: Number
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "faults":
|
case "faults":
|
||||||
q = query.sanitizeQueryTypes(q, {
|
q = query.sanitizeQueryTypes(q, {
|
||||||
timestamp: (v) => new Date(v),
|
timestamp: v => new Date(v),
|
||||||
retries: Number
|
retries: Number
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -582,7 +596,7 @@ function listener(request, response) {
|
|||||||
projection[p.trim()] = 1;
|
projection[p.trim()] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cur = collection.find(q, projection, {batchSize: 50});
|
const cur = collection.find(q, projection, { batchSize: 50 });
|
||||||
|
|
||||||
if (urlParts.query.sort) {
|
if (urlParts.query.sort) {
|
||||||
const s = JSON.parse(urlParts.query.sort);
|
const s = JSON.parse(urlParts.query.sort);
|
||||||
@@ -590,27 +604,24 @@ function listener(request, response) {
|
|||||||
for (let [k, v] of Object.entries(s)) {
|
for (let [k, v] of Object.entries(s)) {
|
||||||
if (k[k.lastIndexOf(".") + 1] !== "_" && collectionName === "devices")
|
if (k[k.lastIndexOf(".") + 1] !== "_" && collectionName === "devices")
|
||||||
sort[`${k}._value`] = v;
|
sort[`${k}._value`] = v;
|
||||||
else
|
else sort[k] = v;
|
||||||
sort[k] = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur.sort(sort);
|
cur.sort(sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlParts.query.skip)
|
if (urlParts.query.skip) cur.skip(parseInt(urlParts.query.skip));
|
||||||
cur.skip(parseInt(urlParts.query.skip));
|
|
||||||
|
|
||||||
let limit;
|
let limit;
|
||||||
if (urlParts.query.limit)
|
if (urlParts.query.limit)
|
||||||
cur.limit(limit = parseInt(urlParts.query.limit));
|
cur.limit((limit = parseInt(urlParts.query.limit)));
|
||||||
|
|
||||||
cur.count(false, (err, total) => {
|
cur.count(false, (err, total) => {
|
||||||
if (err)
|
if (err) return throwError(err);
|
||||||
return throwError(err);
|
|
||||||
|
|
||||||
|
|
||||||
response.writeHead(200, {
|
response.writeHead(200, {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"total": total
|
total: total
|
||||||
});
|
});
|
||||||
|
|
||||||
if (request.method === "HEAD") {
|
if (request.method === "HEAD") {
|
||||||
@@ -627,8 +638,7 @@ function listener(request, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
if (i++)
|
if (i++) response.write(",\n");
|
||||||
response.write(",\n");
|
|
||||||
response.write(JSON.stringify(item));
|
response.write(JSON.stringify(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-46
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -18,25 +18,20 @@
|
|||||||
*/
|
*/
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
class PathSet {
|
class PathSet {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.lengthIndex = [];
|
this.lengthIndex = [];
|
||||||
this.fragmentIndex = [];
|
this.fragmentIndex = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get depth() {
|
get depth() {
|
||||||
return this.lengthIndex.length;
|
return this.lengthIndex.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
add(path) {
|
add(path) {
|
||||||
let p = this.get(path);
|
let p = this.get(path);
|
||||||
|
|
||||||
if (p)
|
if (p) return p;
|
||||||
return p;
|
|
||||||
|
|
||||||
if (!this.lengthIndex[path.length])
|
if (!this.lengthIndex[path.length])
|
||||||
this.lengthIndex[path.length] = new Set();
|
this.lengthIndex[path.length] = new Set();
|
||||||
@@ -44,13 +39,11 @@ class PathSet {
|
|||||||
this.lengthIndex[path.length].add(path);
|
this.lengthIndex[path.length].add(path);
|
||||||
|
|
||||||
let wildcard = 0;
|
let wildcard = 0;
|
||||||
for (let i = 0; i < path.length; ++ i) {
|
for (let i = 0; i < path.length; ++i) {
|
||||||
let fragment = path[i];
|
let fragment = path[i];
|
||||||
if (fragment === "*")
|
if (fragment === "*") wildcard = wildcard | (1 << i);
|
||||||
wildcard = wildcard | (1 << i);
|
|
||||||
|
|
||||||
if (!this.fragmentIndex[i])
|
if (!this.fragmentIndex[i]) this.fragmentIndex[i] = {};
|
||||||
this.fragmentIndex[i] = {};
|
|
||||||
|
|
||||||
if (!this.fragmentIndex[i][fragment])
|
if (!this.fragmentIndex[i][fragment])
|
||||||
this.fragmentIndex[i][fragment] = new Set();
|
this.fragmentIndex[i][fragment] = new Set();
|
||||||
@@ -66,31 +59,26 @@ class PathSet {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get(pattern) {
|
get(pattern) {
|
||||||
if (!this.lengthIndex[pattern.length])
|
if (!this.lengthIndex[pattern.length]) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
if (this.lengthIndex[pattern.length].has(pattern))
|
if (this.lengthIndex[pattern.length].has(pattern)) return pattern;
|
||||||
return pattern;
|
|
||||||
|
|
||||||
let sets = [];
|
let sets = [];
|
||||||
|
|
||||||
sets.push(this.lengthIndex[pattern.length]);
|
sets.push(this.lengthIndex[pattern.length]);
|
||||||
|
|
||||||
for (let i = 0; i < pattern.length; ++ i) {
|
for (let i = 0; i < pattern.length; ++i) {
|
||||||
if (!this.fragmentIndex[i])
|
if (!this.fragmentIndex[i]) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
let fragment = pattern[i];
|
let fragment = pattern[i];
|
||||||
|
|
||||||
if (!this.fragmentIndex[i][fragment])
|
if (!this.fragmentIndex[i][fragment]) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
sets.push(this.fragmentIndex[i][fragment]);
|
sets.push(this.fragmentIndex[i][fragment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
sets.sort(function(a, b) {
|
sets.sort((a, b) => {
|
||||||
return a.size - b.size;
|
return a.size - b.size;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -99,36 +87,31 @@ class PathSet {
|
|||||||
for (let s of sets) {
|
for (let s of sets) {
|
||||||
if (!s.has(path)) {
|
if (!s.has(path)) {
|
||||||
path = null;
|
path = null;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path)
|
|
||||||
return path;
|
if (path) return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
find(pattern, superset = false, subset = false, depth = pattern.length) {
|
find(pattern, superset = false, subset = false, depth = pattern.length) {
|
||||||
let res = [];
|
let res = [];
|
||||||
let groups = [[]];
|
let groups = [[]];
|
||||||
|
|
||||||
for (let i = pattern.length; i <= depth; ++ i)
|
for (let i = pattern.length; i <= depth; ++i)
|
||||||
if (this.lengthIndex[i])
|
if (this.lengthIndex[i]) groups[0].push(this.lengthIndex[i]);
|
||||||
groups[0].push(this.lengthIndex[i]);
|
|
||||||
|
|
||||||
if (!groups[0].length)
|
if (!groups[0].length) return res;
|
||||||
return res;
|
|
||||||
|
|
||||||
for (let i = 0; i < pattern.length; ++ i) {
|
for (let i = 0; i < pattern.length; ++i) {
|
||||||
if (!this.fragmentIndex[i])
|
if (!this.fragmentIndex[i]) return res;
|
||||||
return res;
|
|
||||||
|
|
||||||
let fragment = pattern[i];
|
let fragment = pattern[i];
|
||||||
|
|
||||||
if (fragment === "*" && subset)
|
if (fragment === "*" && subset) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
let g = [];
|
let g = [];
|
||||||
|
|
||||||
@@ -138,15 +121,20 @@ class PathSet {
|
|||||||
if (fragment !== "*" && superset && this.fragmentIndex[i]["*"])
|
if (fragment !== "*" && superset && this.fragmentIndex[i]["*"])
|
||||||
g.push(this.fragmentIndex[i]["*"]);
|
g.push(this.fragmentIndex[i]["*"]);
|
||||||
|
|
||||||
if (!g.length)
|
if (!g.length) return res;
|
||||||
return res;
|
|
||||||
|
|
||||||
groups.push(g);
|
groups.push(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.sort(function(a, b) {
|
groups.sort((a, b) => {
|
||||||
return a.reduce(function(prev, cur) {return prev + cur.size;}, 0) -
|
return (
|
||||||
b.reduce(function(prev, cur) {return prev + cur.size;}, 0);
|
a.reduce((prev, cur) => {
|
||||||
|
return prev + cur.size;
|
||||||
|
}, 0) -
|
||||||
|
b.reduce((prev, cur) => {
|
||||||
|
return prev + cur.size;
|
||||||
|
}, 0)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let smallestGroup = groups.shift();
|
let smallestGroup = groups.shift();
|
||||||
@@ -160,20 +148,18 @@ class PathSet {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
path = null;
|
path = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path)
|
if (path) res.push(path);
|
||||||
res.push(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = PathSet;
|
module.exports = PathSet;
|
||||||
+62
-98
@@ -21,21 +21,16 @@
|
|||||||
const common = require("./common");
|
const common = require("./common");
|
||||||
|
|
||||||
function stringToRegexp(input, flags) {
|
function stringToRegexp(input, flags) {
|
||||||
if (input.indexOf("*") === -1)
|
if (input.indexOf("*") === -1) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
let output = input.replace(/[\[\]\\\^\$\.\|\?\+\(\)]/, "\\$&");
|
let output = input.replace(/[[\]\\^$.|?+()]/, "\\$&");
|
||||||
if (output[0] === "*")
|
if (output[0] === "*") output = output.replace(/^\*+/g, "");
|
||||||
output = output.replace(/^\*+/g, "");
|
else output = "^" + output;
|
||||||
else
|
|
||||||
output = "^" + output;
|
|
||||||
|
|
||||||
if (output[output.length - 1] === "*")
|
if (output[output.length - 1] === "*") output = output.replace(/\*+$/g, "");
|
||||||
output = output.replace(/\*+$/g, "");
|
else output = output + "$";
|
||||||
else
|
|
||||||
output = output + "$";
|
|
||||||
|
|
||||||
output = output.replace(/[\*]/, ".*");
|
output = output.replace(/[*]/, ".*");
|
||||||
return new RegExp(output, flags);
|
return new RegExp(output, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,19 +38,15 @@ function normalize(input) {
|
|||||||
if (common.typeOf(input) === common.STRING_TYPE) {
|
if (common.typeOf(input) === common.STRING_TYPE) {
|
||||||
const vals = [input];
|
const vals = [input];
|
||||||
let m = /^\/(.*?)\/(g?i?m?y?)$/.exec(input);
|
let m = /^\/(.*?)\/(g?i?m?y?)$/.exec(input);
|
||||||
if (m)
|
if (m) vals.push({ $regex: new RegExp(m[1], m[2]) });
|
||||||
vals.push({"$regex": new RegExp(m[1], m[2])});
|
|
||||||
|
|
||||||
if (+input === parseFloat(input))
|
if (+input === parseFloat(input)) vals.push(+input);
|
||||||
vals.push(+input);
|
|
||||||
|
|
||||||
let d = new Date(input);
|
let d = new Date(input);
|
||||||
if (input.length >= 8 && d.getFullYear() > 1983)
|
if (input.length >= 8 && d.getFullYear() > 1983) vals.push(d);
|
||||||
vals.push(d);
|
|
||||||
|
|
||||||
let r = stringToRegexp(input);
|
let r = stringToRegexp(input);
|
||||||
if (r !== false)
|
if (r !== false) vals.push({ $regex: r });
|
||||||
vals.push({"$regex": r});
|
|
||||||
|
|
||||||
return vals;
|
return vals;
|
||||||
}
|
}
|
||||||
@@ -64,16 +55,13 @@ function normalize(input) {
|
|||||||
|
|
||||||
function expandValue(value) {
|
function expandValue(value) {
|
||||||
if (common.typeOf(value) === common.ARRAY_TYPE) {
|
if (common.typeOf(value) === common.ARRAY_TYPE) {
|
||||||
const a = [];
|
let a = [];
|
||||||
for (let j of value)
|
for (let j of value) a = a.conact(expandValue(j));
|
||||||
a = a.conact(expandValue(j));
|
|
||||||
return [a];
|
return [a];
|
||||||
} else if (common.typeOf(value) !== common.OBJECT_TYPE) {
|
} else if (common.typeOf(value) !== common.OBJECT_TYPE) {
|
||||||
const n = normalize(value);
|
const n = normalize(value);
|
||||||
if (common.typeOf(n) !== common.ARRAY_TYPE)
|
if (common.typeOf(n) !== common.ARRAY_TYPE) return [n];
|
||||||
return [n];
|
else return n;
|
||||||
else
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const objs = [];
|
const objs = [];
|
||||||
@@ -89,14 +77,12 @@ function expandValue(value) {
|
|||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < indices.length) {
|
while (i < indices.length) {
|
||||||
const obj = {};
|
const obj = {};
|
||||||
for (let j = 0; j < keys.length; ++ j)
|
for (let j = 0; j < keys.length; ++j) obj[keys[j]] = values[j][indices[j]];
|
||||||
obj[keys[j]] = values[j][indices[j]];
|
|
||||||
objs.push(obj);
|
objs.push(obj);
|
||||||
|
|
||||||
for (i = 0; i < indices.length; ++ i) {
|
for (i = 0; i < indices.length; ++i) {
|
||||||
indices[i] += 1;
|
indices[i] += 1;
|
||||||
if (indices[i] < values[i].length)
|
if (indices[i] < values[i].length) break;
|
||||||
break;
|
|
||||||
indices[i] = 0;
|
indices[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,11 +93,10 @@ function permute(param, val) {
|
|||||||
const conditions = [];
|
const conditions = [];
|
||||||
const values = expandValue(val);
|
const values = expandValue(val);
|
||||||
|
|
||||||
if (param[param.lastIndexOf(".") + 1] !== "_")
|
if (param[param.lastIndexOf(".") + 1] !== "_") param += "._value";
|
||||||
param += "._value";
|
|
||||||
|
|
||||||
for (let v of values) {
|
for (let v of values) {
|
||||||
const obj = {}
|
const obj = {};
|
||||||
obj[param] = v;
|
obj[param] = v;
|
||||||
conditions.push(obj);
|
conditions.push(obj);
|
||||||
}
|
}
|
||||||
@@ -122,7 +107,8 @@ function permute(param, val) {
|
|||||||
function expand(query) {
|
function expand(query) {
|
||||||
const newQuery = {};
|
const newQuery = {};
|
||||||
for (let [k, v] of Object.entries(query)) {
|
for (let [k, v] of Object.entries(query)) {
|
||||||
if (k[0] === "$") { // Operator
|
if (k[0] === "$") {
|
||||||
|
// Operator
|
||||||
newQuery[k] = v.map(e => expand(e));
|
newQuery[k] = v.map(e => expand(e));
|
||||||
} else {
|
} else {
|
||||||
const conditions = permute(k, v);
|
const conditions = permute(k, v);
|
||||||
@@ -131,16 +117,16 @@ function expand(query) {
|
|||||||
if (v && (v["$ne"] != null || v["$not"] != null)) {
|
if (v && (v["$ne"] != null || v["$not"] != null)) {
|
||||||
if (Object.keys(v).length > 1)
|
if (Object.keys(v).length > 1)
|
||||||
throw new Error("Cannot mix $ne or $not with other operators");
|
throw new Error("Cannot mix $ne or $not with other operators");
|
||||||
for (let c of conditions)
|
for (let c of conditions) newQuery["$an"].push(c);
|
||||||
newQuery["$an"].push(c);
|
|
||||||
} else {
|
} else {
|
||||||
newQuery["$and"].push({"$or": conditions});
|
newQuery["$and"].push({ $or: conditions });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Object.assign(newQuery, conditions[0]);
|
Object.assign(newQuery, conditions[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newQuery;
|
return newQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,16 +135,13 @@ function testExpressions(params, expressions, lop) {
|
|||||||
const res = test(params, f);
|
const res = test(params, f);
|
||||||
switch (lop) {
|
switch (lop) {
|
||||||
case "$and":
|
case "$and":
|
||||||
if (!res)
|
if (!res) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case "$or":
|
case "$or":
|
||||||
if (res)
|
if (res) return true;
|
||||||
return true;
|
|
||||||
break;
|
break;
|
||||||
case "$nor":
|
case "$nor":
|
||||||
if (res)
|
if (res) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,20 +160,18 @@ function testExpressions(params, expressions, lop) {
|
|||||||
function test(params, query) {
|
function test(params, query) {
|
||||||
let res;
|
let res;
|
||||||
for (let [k, v] of Object.entries(query)) {
|
for (let [k, v] of Object.entries(query)) {
|
||||||
if (k.charAt(0) === "$") { // Logical operator
|
if (k.charAt(0) === "$") {
|
||||||
|
// Logical operator
|
||||||
res = testExpressions(params, v, k);
|
res = testExpressions(params, v, k);
|
||||||
} else {
|
} else {
|
||||||
const value = params[k];
|
const value = params[k];
|
||||||
if (common.typeOf(v) !== common.OBJECT_TYPE) {
|
if (common.typeOf(v) !== common.OBJECT_TYPE) {
|
||||||
// TODO comparing array to regex, array to array, and object to object
|
|
||||||
if (common.typeOf(value) === common.ARRAY_TYPE) {
|
if (common.typeOf(value) === common.ARRAY_TYPE) {
|
||||||
|
// TODO comparing array to regex, array to array, and object to object
|
||||||
res = value.indexOf(v) !== -1;
|
res = value.indexOf(v) !== -1;
|
||||||
} else {
|
} else {
|
||||||
if (common.typeOf(v) === common.REGEXP_TYPE) {
|
if (common.typeOf(v) === common.REGEXP_TYPE) res = v.test(value);
|
||||||
res = v.test(value);
|
else res = v === value;
|
||||||
} else {
|
|
||||||
res = v === value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let [k2, v2] of Object.entries(v)) {
|
for (let [k2, v2] of Object.entries(v)) {
|
||||||
@@ -198,8 +179,7 @@ function test(params, query) {
|
|||||||
case "$ne":
|
case "$ne":
|
||||||
if (common.typeOf(value) === common.ARRAY_TYPE)
|
if (common.typeOf(value) === common.ARRAY_TYPE)
|
||||||
res = value.indexOf(v2) === -1;
|
res = value.indexOf(v2) === -1;
|
||||||
else
|
else res = value !== v2;
|
||||||
res = value !== v2;
|
|
||||||
break;
|
break;
|
||||||
case "$lt":
|
case "$lt":
|
||||||
res = value < v2;
|
res = value < v2;
|
||||||
@@ -218,16 +198,12 @@ function test(params, query) {
|
|||||||
break;
|
break;
|
||||||
case "$in":
|
case "$in":
|
||||||
throw new Error("Operator not supported");
|
throw new Error("Operator not supported");
|
||||||
break;
|
|
||||||
case "$nin":
|
case "$nin":
|
||||||
throw new Error("Operator not supported");
|
throw new Error("Operator not supported");
|
||||||
break;
|
|
||||||
case "$all":
|
case "$all":
|
||||||
throw new Error("Operator not supported");
|
throw new Error("Operator not supported");
|
||||||
break;
|
|
||||||
case "$exists":
|
case "$exists":
|
||||||
throw new Error("Operator not supported");
|
throw new Error("Operator not supported");
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new Error("Operator not supported");
|
throw new Error("Operator not supported");
|
||||||
}
|
}
|
||||||
@@ -235,8 +211,7 @@ function test(params, query) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res)
|
if (!res) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -246,49 +221,41 @@ function matchType(src, dst) {
|
|||||||
if (type === "string") {
|
if (type === "string") {
|
||||||
return String(dst);
|
return String(dst);
|
||||||
} else if (type === "number") {
|
} else if (type === "number") {
|
||||||
if (+dst === parseFloat(dst))
|
if (+dst === parseFloat(dst)) return +dst;
|
||||||
return +dst;
|
else if (!isNaN(Date.parse(dst))) return Date.parse(dst);
|
||||||
else if (!isNaN(Date.parse(dst)))
|
|
||||||
return Date.parse(dst);
|
|
||||||
} else if (type === "boolean") {
|
} else if (type === "boolean") {
|
||||||
const v = String(dst).trim().toLowerCase();
|
const v = String(dst)
|
||||||
if (v === "true" || v === "1")
|
.trim()
|
||||||
return true;
|
.toLowerCase();
|
||||||
else if (v === "false" || v === "0")
|
if (v === "true" || v === "1") return true;
|
||||||
return false;
|
else if (v === "false" || v === "0") return false;
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
function testFilter(obj, filter) {
|
function testFilter(obj, filter) {
|
||||||
for (let [k, v] of Object.entries(filter)) {
|
for (let [k, v] of Object.entries(filter)) {
|
||||||
const [param, op] = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 2)
|
const [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2);
|
||||||
const val = matchType(obj[param], v);
|
const val = matchType(obj[param], v);
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case "=":
|
case "=":
|
||||||
case undefined:
|
case undefined:
|
||||||
if (obj[param] !== val)
|
if (obj[param] !== val) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case ">":
|
case ">":
|
||||||
if (!(obj[param] > val))
|
if (!(obj[param] > val)) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case ">=":
|
case ">=":
|
||||||
if (!(obj[param] >= val))
|
if (!(obj[param] >= val)) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case "<":
|
case "<":
|
||||||
if (!(obj[param] < val))
|
if (!(obj[param] < val)) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case "<=":
|
case "<=":
|
||||||
if (!(obj[param] <= val))
|
if (!(obj[param] <= val)) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
case "!":
|
case "!":
|
||||||
if (obj[param] === val)
|
if (obj[param] === val) return false;
|
||||||
return false;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognized operator ${op}`);
|
throw new Error(`Unrecognized operator ${op}`);
|
||||||
@@ -298,25 +265,21 @@ function testFilter(obj, filter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertMongoQueryToFilters(query, filters) {
|
function convertMongoQueryToFilters(query, filters) {
|
||||||
filters = filters || {}
|
filters = filters || {};
|
||||||
for (let [k, v] of Object.entries(query)) {
|
for (let [k, v] of Object.entries(query)) {
|
||||||
if (k[0] === "$") {
|
if (k[0] === "$") {
|
||||||
if (k === "$and")
|
if (k === "$and")
|
||||||
for (let vv of v)
|
for (let vv of v) convertMongoQueryToFilters(vv, filters);
|
||||||
convertMongoQueryToFilters(vv, filters);
|
else throw new Error(`Operator ${k} not supported`);
|
||||||
else
|
|
||||||
throw new Error(`Operator ${k} not supported`);
|
|
||||||
} else if (k === "_tags") {
|
} else if (k === "_tags") {
|
||||||
if (common.typeOf(v) === common.OBJECT_TYPE) {
|
if (common.typeOf(v) === common.OBJECT_TYPE) {
|
||||||
for (let [kk, vv] of Object.entries(v)) {
|
for (let [kk, vv] of Object.entries(v)) {
|
||||||
vv = vv.replace(/[^a-zA-Z0-9\-]+/g, "_");
|
vv = vv.replace(/[^a-zA-Z0-9-]+/g, "_");
|
||||||
if (kk === "$ne")
|
if (kk === "$ne") filters[`Tags.${vv}!`] = true;
|
||||||
filters[`Tags.${vv}!`] = true;
|
else throw new Error(`Operator ${kk} not supported`);
|
||||||
else
|
|
||||||
throw new Error(`Operator ${kk} not supported`);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v = v.replace(/[^a-zA-Z0-9\-]+/g, "_");
|
v = v.replace(/[^a-zA-Z0-9-]+/g, "_");
|
||||||
filters[`Tags.${v}`] = true;
|
filters[`Tags.${v}`] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -379,22 +342,22 @@ function convertMongoQueryToFilters(query, filters) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeQueryTypes(query, types) {
|
function sanitizeQueryTypes(query, types) {
|
||||||
for (let [k, v] of Object.entries(query)) {
|
for (let [k, v] of Object.entries(query)) {
|
||||||
if (k[0] === "$") { // Logical operator
|
if (k[0] === "$") {
|
||||||
for (let vv of v)
|
// Logical operator
|
||||||
sanitizeQueryTypes(vv, types);
|
for (let vv of v) sanitizeQueryTypes(vv, types);
|
||||||
} else if (k in types) {
|
} else if (k in types) {
|
||||||
if (common.typeOf(v) === common.OBJECT_TYPE) {
|
if (common.typeOf(v) === common.OBJECT_TYPE) {
|
||||||
for (let [kk, vv] of Object.entries(v)) {
|
for (let [kk, vv] of Object.entries(v)) {
|
||||||
switch (kk) {
|
switch (kk) {
|
||||||
case "$in":
|
case "$in":
|
||||||
case "$nin":
|
case "$nin":
|
||||||
for (let i = 0; i < vv.length; ++ i)
|
for (let i = 0; i < vv.length; ++i) vv[i] = types[k](vv[i]);
|
||||||
vv[i] = types[k](vv[i]);
|
|
||||||
break;
|
break;
|
||||||
case "$eq":
|
case "$eq":
|
||||||
case "$gt":
|
case "$gt":
|
||||||
@@ -417,6 +380,7 @@ function sanitizeQueryTypes(query, types) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+150
-129
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -28,7 +28,6 @@ const extensions = require("./extensions");
|
|||||||
const logger = require("./logger");
|
const logger = require("./logger");
|
||||||
const scheduling = require("./scheduling");
|
const scheduling = require("./scheduling");
|
||||||
|
|
||||||
|
|
||||||
// Used for throwing to exit user script and commit
|
// Used for throwing to exit user script and commit
|
||||||
const COMMIT = Symbol();
|
const COMMIT = Symbol();
|
||||||
|
|
||||||
@@ -49,24 +48,20 @@ function runExtension(sessionContext, key, extCall, callback) {
|
|||||||
|
|
||||||
if (re[key]) {
|
if (re[key]) {
|
||||||
re[key].push(callback);
|
re[key].push(callback);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
re[key] = [callback];
|
re[key] = [callback];
|
||||||
extensions.run(extCall, function(err, fault, res) {
|
extensions.run(extCall, (err, fault, res) => {
|
||||||
const callbacks = re[key];
|
const callbacks = re[key];
|
||||||
delete re[key];
|
delete re[key];
|
||||||
if (!err && !fault)
|
if (!err && !fault) sessionContext.extensionsCache[key] = res;
|
||||||
sessionContext.extensionsCache[key] = res;
|
for (let c of callbacks) c(err, fault);
|
||||||
for (let c of callbacks)
|
|
||||||
c(err, fault);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SandboxDate {
|
class SandboxDate {
|
||||||
constructor() {
|
constructor() {
|
||||||
if (arguments.length)
|
if (arguments.length) return new Date(...arguments);
|
||||||
return new Date(...arguments);
|
|
||||||
|
|
||||||
return new Date(state.sessionContext.timestamp);
|
return new Date(state.sessionContext.timestamp);
|
||||||
}
|
}
|
||||||
@@ -75,120 +70,128 @@ class SandboxDate {
|
|||||||
let t = state.sessionContext.timestamp;
|
let t = state.sessionContext.timestamp;
|
||||||
|
|
||||||
if (typeof intervalOrCron === "number") {
|
if (typeof intervalOrCron === "number") {
|
||||||
|
if (variance == null) variance = intervalOrCron;
|
||||||
if (variance == null)
|
|
||||||
variance = intervalOrCron;
|
|
||||||
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
if (variance)
|
if (variance)
|
||||||
offset = scheduling.variance(state.sessionContext.deviceId, variance);
|
offset = scheduling.variance(state.sessionContext.deviceId, variance);
|
||||||
|
|
||||||
t = scheduling.interval(t, intervalOrCron, offset);
|
t = scheduling.interval(t, intervalOrCron, offset);
|
||||||
}
|
} else if (typeof intervalOrCron === "string") {
|
||||||
else if (typeof intervalOrCron === "string") {
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
if (variance)
|
if (variance)
|
||||||
offset = scheduling.variance(state.sessionContext.deviceId, variance);
|
offset = scheduling.variance(state.sessionContext.deviceId, variance);
|
||||||
let cron = scheduling.parseCron(intervalOrCron);
|
let cron = scheduling.parseCron(intervalOrCron);
|
||||||
t = scheduling.cron(t, cron, offset)[0];
|
t = scheduling.cron(t, cron, offset)[0];
|
||||||
}
|
} else if (intervalOrCron) {
|
||||||
else if (intervalOrCron) {
|
|
||||||
throw new Error("Invalid Date.now() argument");
|
throw new Error("Invalid Date.now() argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function random() {
|
function random() {
|
||||||
if (!state.rng)
|
if (!state.rng) state.rng = seedrandom(state.sessionContext.deviceId);
|
||||||
state.rng = seedrandom(state.sessionContext.deviceId);
|
|
||||||
|
|
||||||
return state.rng();
|
return state.rng();
|
||||||
}
|
}
|
||||||
|
|
||||||
random.seed = function(s) {
|
random.seed = function(s) {
|
||||||
state.rng = seedrandom(s);
|
state.rng = seedrandom(s);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
class ParameterWrapper {
|
class ParameterWrapper {
|
||||||
constructor(path, attributes, unpacked, unpackedRevision) {
|
constructor(path, attributes, unpacked, unpackedRevision) {
|
||||||
for (let attrName of attributes) {
|
for (let attrName of attributes) {
|
||||||
Object.defineProperty(this, attrName, {get: function() {
|
Object.defineProperty(this, attrName, {
|
||||||
if (state.uncommitted)
|
get: function() {
|
||||||
commit();
|
if (state.uncommitted) commit();
|
||||||
|
|
||||||
|
if (state.revision !== unpackedRevision) {
|
||||||
|
unpackedRevision = state.revision;
|
||||||
|
unpacked = device.unpack(
|
||||||
|
state.sessionContext.deviceData,
|
||||||
|
path,
|
||||||
|
state.revision
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unpacked.length) return undefined;
|
||||||
|
|
||||||
|
let attr = state.sessionContext.deviceData.attributes.get(
|
||||||
|
unpacked[0],
|
||||||
|
state.revision
|
||||||
|
)[attrName];
|
||||||
|
|
||||||
|
if (!attr) return undefined;
|
||||||
|
|
||||||
|
return attr[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(this, "path", {
|
||||||
|
get: function() {
|
||||||
|
if (state.uncommitted) commit();
|
||||||
|
|
||||||
if (state.revision !== unpackedRevision) {
|
if (state.revision !== unpackedRevision) {
|
||||||
unpackedRevision = state.revision;
|
unpackedRevision = state.revision;
|
||||||
unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision);
|
unpacked = device.unpack(
|
||||||
|
state.sessionContext.deviceData,
|
||||||
|
path,
|
||||||
|
state.revision
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unpacked.length)
|
if (!unpacked.length) return undefined;
|
||||||
return undefined;
|
|
||||||
|
|
||||||
let attr = state.sessionContext.deviceData.attributes.get(unpacked[0], state.revision)[attrName];
|
return unpacked[0].join(".");
|
||||||
|
|
||||||
if (!attr)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
return attr[1];
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(this, "path", {get: function() {
|
|
||||||
if (state.uncommitted)
|
|
||||||
commit();
|
|
||||||
|
|
||||||
if (state.revision !== unpackedRevision) {
|
|
||||||
unpackedRevision = state.revision;
|
|
||||||
unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!unpacked.length)
|
Object.defineProperty(this, "size", {
|
||||||
return undefined;
|
get: function() {
|
||||||
|
if (state.uncommitted) commit();
|
||||||
|
|
||||||
return unpacked[0].join(".");
|
if (state.revision !== unpackedRevision) {
|
||||||
}});
|
unpackedRevision = state.revision;
|
||||||
|
unpacked = device.unpack(
|
||||||
|
state.sessionContext.deviceData,
|
||||||
|
path,
|
||||||
|
state.revision
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(this, "size", {get: function() {
|
if (!unpacked.length) return undefined;
|
||||||
if (state.uncommitted)
|
|
||||||
commit();
|
|
||||||
|
|
||||||
if (state.revision !== unpackedRevision) {
|
return unpacked.length;
|
||||||
unpackedRevision = state.revision;
|
|
||||||
unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (!unpacked.length)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
return unpacked.length;
|
|
||||||
}});
|
|
||||||
|
|
||||||
this[Symbol.iterator] = function*() {
|
this[Symbol.iterator] = function*() {
|
||||||
if (state.uncommitted)
|
if (state.uncommitted) commit();
|
||||||
commit();
|
|
||||||
|
|
||||||
if (state.revision !== unpackedRevision) {
|
if (state.revision !== unpackedRevision) {
|
||||||
unpackedRevision = state.revision;
|
unpackedRevision = state.revision;
|
||||||
unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision);
|
unpacked = device.unpack(
|
||||||
|
state.sessionContext.deviceData,
|
||||||
|
path,
|
||||||
|
state.revision
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let p of unpacked)
|
for (let p of unpacked)
|
||||||
yield new ParameterWrapper(p, attributes, [p], state.revision);
|
yield new ParameterWrapper(p, attributes, [p], state.revision);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function declare(path, timestamps, values) {
|
function declare(path, timestamps, values) {
|
||||||
state.uncommitted = true;
|
state.uncommitted = true;
|
||||||
if (!timestamps)
|
if (!timestamps) timestamps = {};
|
||||||
timestamps = {};
|
|
||||||
|
|
||||||
if (!values)
|
if (!values) values = {};
|
||||||
values = {};
|
|
||||||
|
|
||||||
let parsedPath = common.parsePath(path);
|
let parsedPath = common.parsePath(path);
|
||||||
|
|
||||||
@@ -196,25 +199,23 @@ function declare(path, timestamps, values) {
|
|||||||
let attrs = {};
|
let attrs = {};
|
||||||
|
|
||||||
for (let attrName in values) {
|
for (let attrName in values) {
|
||||||
if (!timestamps[attrName])
|
if (!timestamps[attrName]) timestamps[attrName] = 1;
|
||||||
timestamps[attrName] = 1;
|
|
||||||
|
|
||||||
if (attrName === "path")
|
if (attrName === "path") {
|
||||||
declaration[3] = values.path;
|
declaration[3] = values.path;
|
||||||
else {
|
} else {
|
||||||
attrs[attrName] = 1;
|
attrs[attrName] = 1;
|
||||||
declaration[4] = declaration[4] || {};
|
declaration[4] = declaration[4] || {};
|
||||||
if (attrName === "value" && !Array.isArray(values.value))
|
if (attrName === "value" && !Array.isArray(values.value))
|
||||||
declaration[4].value = [values.value];
|
declaration[4].value = [values.value];
|
||||||
else
|
else declaration[4][attrName] = values[attrName];
|
||||||
declaration[4][attrName] = values[attrName];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let attrName in timestamps) {
|
for (let attrName in timestamps) {
|
||||||
if (attrName === "path")
|
if (attrName === "path") {
|
||||||
declaration[1] = timestamps.path;
|
declaration[1] = timestamps.path;
|
||||||
else {
|
} else {
|
||||||
attrs[attrName] = 1;
|
attrs[attrName] = 1;
|
||||||
declaration[2] = declaration[2] || {};
|
declaration[2] = declaration[2] || {};
|
||||||
declaration[2][attrName] = timestamps[attrName];
|
declaration[2][attrName] = timestamps[attrName];
|
||||||
@@ -226,7 +227,6 @@ function declare(path, timestamps, values) {
|
|||||||
return new ParameterWrapper(parsedPath, Object.keys(attrs));
|
return new ParameterWrapper(parsedPath, Object.keys(attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function clear(path, timestamp, attributes) {
|
function clear(path, timestamp, attributes) {
|
||||||
state.uncommitted = true;
|
state.uncommitted = true;
|
||||||
|
|
||||||
@@ -234,20 +234,21 @@ function clear(path, timestamp, attributes) {
|
|||||||
state.clear.push([common.parsePath(path), timestamp, attributes]);
|
state.clear.push([common.parsePath(path), timestamp, attributes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function commit() {
|
function commit() {
|
||||||
++ state.revision;
|
++state.revision;
|
||||||
state.uncommitted = false;
|
state.uncommitted = false;
|
||||||
|
|
||||||
if (state.revision === state.maxRevision + 1)
|
if (state.revision === state.maxRevision + 1) {
|
||||||
throw COMMIT;
|
throw COMMIT;
|
||||||
else if (state.revision > state.maxRevision + 1)
|
} else if (state.revision > state.maxRevision + 1) {
|
||||||
throw new Error("Declare function should not be called from within a try/catch block");
|
throw new Error(
|
||||||
|
"Declare function should not be called from within a try/catch block"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ext() {
|
function ext() {
|
||||||
++ state.extCounter;
|
++state.extCounter;
|
||||||
let extCall = Array.from(arguments).map(String);
|
let extCall = Array.from(arguments).map(String);
|
||||||
let key = `${state.revision}: ${JSON.stringify(extCall)}`;
|
let key = `${state.revision}: ${JSON.stringify(extCall)}`;
|
||||||
|
|
||||||
@@ -258,7 +259,6 @@ function ext() {
|
|||||||
throw EXT;
|
throw EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function log(msg, meta) {
|
function log(msg, meta) {
|
||||||
if (state.revision === state.maxRevision && state.extCounter >= 0) {
|
if (state.revision === state.maxRevision && state.extCounter >= 0) {
|
||||||
const details = Object.assign({}, meta, {
|
const details = Object.assign({}, meta, {
|
||||||
@@ -277,26 +277,22 @@ function log(msg, meta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(context, "Date", { value: SandboxDate });
|
||||||
Object.defineProperty(context, "Date", {value: SandboxDate});
|
Object.defineProperty(context, "declare", { value: declare });
|
||||||
Object.defineProperty(context, "declare", {value: declare});
|
Object.defineProperty(context, "clear", { value: clear });
|
||||||
Object.defineProperty(context, "clear", {value: clear});
|
Object.defineProperty(context, "commit", { value: commit });
|
||||||
Object.defineProperty(context, "commit", {value: commit});
|
Object.defineProperty(context, "ext", { value: ext });
|
||||||
Object.defineProperty(context, "ext", {value: ext});
|
Object.defineProperty(context, "log", { value: log });
|
||||||
Object.defineProperty(context, "log", {value: log});
|
|
||||||
|
|
||||||
// Monkey-patch Math.random() to make it deterministic
|
// Monkey-patch Math.random() to make it deterministic
|
||||||
context.random = random;
|
context.random = random;
|
||||||
vm.runInContext("Math.random = random;", context);
|
vm.runInContext("Math.random = random;", context);
|
||||||
delete context.random;
|
delete context.random;
|
||||||
|
|
||||||
|
|
||||||
function errorToFault(err) {
|
function errorToFault(err) {
|
||||||
if (!err)
|
if (!err) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!err.name)
|
if (!err.name) return { code: "script", message: `${err}` };
|
||||||
return {code: "script", message: `${err}`};
|
|
||||||
|
|
||||||
let fault = {
|
let fault = {
|
||||||
code: `script.${err.name}`,
|
code: `script.${err.name}`,
|
||||||
@@ -310,7 +306,9 @@ function errorToFault(err) {
|
|||||||
if (err.stack) {
|
if (err.stack) {
|
||||||
fault.detail.stack = err.stack;
|
fault.detail.stack = err.stack;
|
||||||
// Trim the stack trace at the self-executing anonymous wrapper function
|
// Trim the stack trace at the self-executing anonymous wrapper function
|
||||||
let stackTrimIndex = fault.detail.stack.match(/\s+at\s[^\s]+\s+at\s[^\s]+\s\(vm\.js.+\)/);
|
let stackTrimIndex = fault.detail.stack.match(
|
||||||
|
/\s+at\s[^\s]+\s+at\s[^\s]+\s\(vm\.js.+\)/
|
||||||
|
);
|
||||||
if (stackTrimIndex)
|
if (stackTrimIndex)
|
||||||
fault.detail.stack = fault.detail.stack.slice(0, stackTrimIndex.index);
|
fault.detail.stack = fault.detail.stack.slice(0, stackTrimIndex.index);
|
||||||
}
|
}
|
||||||
@@ -318,8 +316,15 @@ function errorToFault(err) {
|
|||||||
return fault;
|
return fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function run(
|
||||||
function run(script, globals, sessionContext, startRevision, maxRevision, callback, extCounter = 0) {
|
script,
|
||||||
|
globals,
|
||||||
|
sessionContext,
|
||||||
|
startRevision,
|
||||||
|
maxRevision,
|
||||||
|
callback,
|
||||||
|
extCounter = 0
|
||||||
|
) {
|
||||||
state = {
|
state = {
|
||||||
sessionContext: sessionContext,
|
sessionContext: sessionContext,
|
||||||
revision: startRevision,
|
revision: startRevision,
|
||||||
@@ -332,24 +337,19 @@ function run(script, globals, sessionContext, startRevision, maxRevision, callba
|
|||||||
extCounter: extCounter
|
extCounter: extCounter
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let n of Object.keys(context))
|
for (let n of Object.keys(context)) delete context[n];
|
||||||
delete context[n];
|
|
||||||
|
|
||||||
Object.assign(context, globals);
|
Object.assign(context, globals);
|
||||||
|
|
||||||
let ret, status;
|
let ret, status;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ret = script.runInContext(context, {displayErrors: false});
|
ret = script.runInContext(context, { displayErrors: false });
|
||||||
status = 0;
|
status = 0;
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
if (err === COMMIT) status = 1;
|
||||||
if (err === COMMIT)
|
else if (err === EXT) status = 2;
|
||||||
status = 1;
|
else return callback(null, errorToFault(err));
|
||||||
else if (err === EXT)
|
|
||||||
status = 2;
|
|
||||||
else
|
|
||||||
return callback(null, errorToFault(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _state = state;
|
let _state = state;
|
||||||
@@ -363,26 +363,47 @@ function run(script, globals, sessionContext, startRevision, maxRevision, callba
|
|||||||
let counter = 3;
|
let counter = 3;
|
||||||
for (let key of Object.keys(_state.extensions)) {
|
for (let key of Object.keys(_state.extensions)) {
|
||||||
counter += 2;
|
counter += 2;
|
||||||
runExtension(_state.sessionContext, key, _state.extensions[key], function(err, fault) {
|
runExtension(
|
||||||
if (err || fault) {
|
_state.sessionContext,
|
||||||
if (counter & 1)
|
key,
|
||||||
callback(err, fault);
|
_state.extensions[key],
|
||||||
return counter = 0;
|
(err, fault) => {
|
||||||
|
if (err || fault) {
|
||||||
|
if (counter & 1) callback(err, fault);
|
||||||
|
return (counter = 0);
|
||||||
|
}
|
||||||
|
if ((counter -= 2) === 1) {
|
||||||
|
if (status === 2) {
|
||||||
|
return run.apply(null, args);
|
||||||
|
} else {
|
||||||
|
return callback(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
_state.clear,
|
||||||
|
_state.declarations,
|
||||||
|
status === 0,
|
||||||
|
ret
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((counter -= 2) === 1)
|
);
|
||||||
if (status === 2)
|
|
||||||
return run.apply(null, args);
|
|
||||||
else
|
|
||||||
return callback(null, null, _state.clear, _state.declarations, status === 0, ret);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((counter -= 2) === 1)
|
if ((counter -= 2) === 1) {
|
||||||
if (status === 2)
|
if (status === 2) {
|
||||||
return run.apply(null, args);
|
return run.apply(null, args);
|
||||||
else
|
} else {
|
||||||
return callback(null, null, _state.clear, _state.declarations, status === 0, ret);
|
return callback(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
_state.clear,
|
||||||
|
_state.declarations,
|
||||||
|
status === 0,
|
||||||
|
ret
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.run = run;
|
exports.run = run;
|
||||||
+8
-17
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -22,7 +22,6 @@ const crypto = require("crypto");
|
|||||||
|
|
||||||
const later = require("later");
|
const later = require("later");
|
||||||
|
|
||||||
|
|
||||||
function md532(str) {
|
function md532(str) {
|
||||||
const digest = crypto
|
const digest = crypto
|
||||||
.createHash("md5")
|
.createHash("md5")
|
||||||
@@ -36,42 +35,34 @@ function md532(str) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function variance(deviceId, vrnc) {
|
||||||
function variance(deviceId, variance) {
|
return (md532(deviceId) >>> 0) % vrnc;
|
||||||
return (md532(deviceId) >>> 0) % variance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function interval(timestamp, intrvl, offset = 0) {
|
||||||
function interval(timestamp, interval, offset = 0) {
|
return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset;
|
||||||
return Math.trunc((timestamp + offset) / interval) * interval - offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function parseCron(cronExp) {
|
function parseCron(cronExp) {
|
||||||
let parts = cronExp.trim().split(/\s+/);
|
let parts = cronExp.trim().split(/\s+/);
|
||||||
if (parts.length === 5)
|
if (parts.length === 5) parts.unshift("*");
|
||||||
parts.unshift("*");
|
|
||||||
|
|
||||||
return later.schedule(later.parse.cron(parts.join(" "), true));
|
return later.schedule(later.parse.cron(parts.join(" "), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function cron(timestamp, schedule, offset = 0) {
|
function cron(timestamp, schedule, offset = 0) {
|
||||||
// TODO later.js doesn't throw erorr if expression is invalid!
|
// TODO later.js doesn't throw erorr if expression is invalid!
|
||||||
const ret = [0, 0];
|
const ret = [0, 0];
|
||||||
|
|
||||||
let prev = schedule.prev(1, new Date(timestamp + offset));
|
let prev = schedule.prev(1, new Date(timestamp + offset));
|
||||||
if (prev)
|
if (prev) ret[0] = prev.setMilliseconds(0) - offset;
|
||||||
ret[0] = prev.setMilliseconds(0) - offset;
|
|
||||||
|
|
||||||
let next = schedule.next(1, new Date(timestamp + offset + 1000));
|
let next = schedule.next(1, new Date(timestamp + offset + 1000));
|
||||||
if (next)
|
if (next) ret[1] = next.setMilliseconds(0) - offset;
|
||||||
ret[1] = next.setMilliseconds(0) - offset;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.variance = variance;
|
exports.variance = variance;
|
||||||
exports.interval = interval;
|
exports.interval = interval;
|
||||||
exports.parseCron = parseCron;
|
exports.parseCron = parseCron;
|
||||||
|
|||||||
+25
-26
@@ -20,8 +20,7 @@
|
|||||||
|
|
||||||
const service = process.argv[2];
|
const service = process.argv[2];
|
||||||
|
|
||||||
if (service == null)
|
if (service == null) throw new Error("Missing argument cwmp, fs, or nbi");
|
||||||
throw new Error("Missing argument cwmp, fs, or nbi");
|
|
||||||
|
|
||||||
const cluster = require("cluster");
|
const cluster = require("cluster");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@@ -39,17 +38,15 @@ const networkInterface = config.get(`${service.toUpperCase()}_INTERFACE`);
|
|||||||
const port = config.get(`${service.toUpperCase()}_PORT`);
|
const port = config.get(`${service.toUpperCase()}_PORT`);
|
||||||
const useHttps = config.get(`${service.toUpperCase()}_SSL`);
|
const useHttps = config.get(`${service.toUpperCase()}_SSL`);
|
||||||
|
|
||||||
const {listener, onConnection} = require(`./${service}`);
|
const { listener, onConnection } = require(`./${service}`);
|
||||||
|
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
function closeServer(timeout, callback) {
|
function closeServer(timeout, callback) {
|
||||||
if (!server)
|
if (!server) return callback();
|
||||||
return callback();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!callback)
|
if (!callback) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// Ignore HTTP requests from connection that may still be open
|
// Ignore HTTP requests from connection that may still be open
|
||||||
server.removeListener("request", listener);
|
server.removeListener("request", listener);
|
||||||
@@ -60,9 +57,8 @@ function closeServer(timeout, callback) {
|
|||||||
setTimeout(cb, 1000);
|
setTimeout(cb, 1000);
|
||||||
}, timeout).unref();
|
}, timeout).unref();
|
||||||
|
|
||||||
server.close(function() {
|
server.close(() => {
|
||||||
if (!callback)
|
if (!callback) return;
|
||||||
return;
|
|
||||||
|
|
||||||
let cb = callback;
|
let cb = callback;
|
||||||
callback = null;
|
callback = null;
|
||||||
@@ -81,12 +77,11 @@ function exit() {
|
|||||||
db.disconnect();
|
db.disconnect();
|
||||||
cache.disconnect();
|
cache.disconnect();
|
||||||
extensions.killAll();
|
extensions.killAll();
|
||||||
if (cluster.worker)
|
if (cluster.worker) cluster.worker.disconnect();
|
||||||
cluster.worker.disconnect();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on("uncaughtException", (err) => {
|
process.on("uncaughtException", err => {
|
||||||
logger.error({
|
logger.error({
|
||||||
message: "Uncaught exception",
|
message: "Uncaught exception",
|
||||||
exception: err,
|
exception: err,
|
||||||
@@ -98,7 +93,10 @@ process.on("uncaughtException", (err) => {
|
|||||||
if (useHttps) {
|
if (useHttps) {
|
||||||
const httpsKey = path.resolve(config.get("CONFIG_DIR"), `${service}.key`);
|
const httpsKey = path.resolve(config.get("CONFIG_DIR"), `${service}.key`);
|
||||||
const httpsCert = path.resolve(config.get("CONFIG_DIR"), `${service}.crt`);
|
const httpsCert = path.resolve(config.get("CONFIG_DIR"), `${service}.crt`);
|
||||||
const httpsCa = path.resolve(config.get("CONFIG_DIR"), `${service}.ca-bundle`);
|
const httpsCa = path.resolve(
|
||||||
|
config.get("CONFIG_DIR"),
|
||||||
|
`${service}.ca-bundle`
|
||||||
|
);
|
||||||
const options = {
|
const options = {
|
||||||
key: fs.readFileSync(httpsKey),
|
key: fs.readFileSync(httpsKey),
|
||||||
cert: fs.readFileSync(httpsCert)
|
cert: fs.readFileSync(httpsCert)
|
||||||
@@ -106,24 +104,25 @@ if (useHttps) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Use intermediate certificates if available
|
// Use intermediate certificates if available
|
||||||
options.ca = fs.readFileSync(httpsCa).toString().match(/\-+BEGIN CERTIFICATE\-+[0-9a-zA-Z\+\-\/\=\s]+?\-+END CERTIFICATE\-+/g);
|
options.ca = fs
|
||||||
} catch (error) {}
|
.readFileSync(httpsCa)
|
||||||
|
.toString()
|
||||||
|
.match(/-+BEGIN CERTIFICATE-+[0-9a-zA-Z+\-/=\s]+?-+END CERTIFICATE-+/g);
|
||||||
|
} catch (error) {
|
||||||
|
// No intermediate certificate
|
||||||
|
}
|
||||||
server = https.createServer(options, listener);
|
server = https.createServer(options, listener);
|
||||||
if (onConnection != null)
|
if (onConnection != null) server.on("secureConnection", onConnection);
|
||||||
server.on("secureConnection", onConnection);
|
|
||||||
} else {
|
} else {
|
||||||
server = http.createServer(listener);
|
server = http.createServer(listener);
|
||||||
if (onConnection != null)
|
if (onConnection != null) server.on("connection", onConnection);
|
||||||
server.on("connection", onConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.connect((err) => {
|
db.connect(err => {
|
||||||
if (err)
|
if (err) throw err;
|
||||||
throw err;
|
|
||||||
|
|
||||||
cache.connect(function(err) {
|
cache.connect(err => {
|
||||||
if (err)
|
if (err) throw err;
|
||||||
throw err;
|
|
||||||
|
|
||||||
server.listen(port, networkInterface);
|
server.listen(port, networkInterface);
|
||||||
});
|
});
|
||||||
|
|||||||
+110
-125
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -28,50 +28,50 @@ const NAMESPACES = {
|
|||||||
"1.0": {
|
"1.0": {
|
||||||
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
"xsd": "http://www.w3.org/2001/XMLSchema",
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
"cwmp": "urn:dslforum-org:cwmp-1-0"
|
cwmp: "urn:dslforum-org:cwmp-1-0"
|
||||||
},
|
},
|
||||||
"1.1": {
|
"1.1": {
|
||||||
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
"xsd": "http://www.w3.org/2001/XMLSchema",
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
"cwmp": "urn:dslforum-org:cwmp-1-1"
|
cwmp: "urn:dslforum-org:cwmp-1-1"
|
||||||
},
|
},
|
||||||
"1.2": {
|
"1.2": {
|
||||||
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
"xsd": "http://www.w3.org/2001/XMLSchema",
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
"cwmp": "urn:dslforum-org:cwmp-1-2"
|
cwmp: "urn:dslforum-org:cwmp-1-2"
|
||||||
},
|
},
|
||||||
"1.3": {
|
"1.3": {
|
||||||
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
"xsd": "http://www.w3.org/2001/XMLSchema",
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
"cwmp": "urn:dslforum-org:cwmp-1-2"
|
cwmp: "urn:dslforum-org:cwmp-1-2"
|
||||||
},
|
},
|
||||||
"1.4": {
|
"1.4": {
|
||||||
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
"xsd": "http://www.w3.org/2001/XMLSchema",
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
"cwmp": "urn:dslforum-org:cwmp-1-3"
|
cwmp: "urn:dslforum-org:cwmp-1-3"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const LIBXMLJS_PARSE_OPTIONS = {
|
const LIBXMLJS_PARSE_OPTIONS = {
|
||||||
"nocdata": true,
|
nocdata: true,
|
||||||
"recover": config.get("XML_RECOVER"),
|
recover: config.get("XML_RECOVER"),
|
||||||
"ignore_enc": config.get("XML_IGNORE_ENC")
|
ignore_enc: config.get("XML_IGNORE_ENC")
|
||||||
};
|
};
|
||||||
|
|
||||||
const LIBXMLJS_SAVE_OPTIONS = {
|
const LIBXMLJS_SAVE_OPTIONS = {
|
||||||
"format": config.get("XML_FORMAT"),
|
format: config.get("XML_FORMAT"),
|
||||||
"declaration": !config.get("XML_NO_DECL"),
|
declaration: !config.get("XML_NO_DECL"),
|
||||||
"selfCloseEmpty": !config.get("XML_NO_EMPTY")
|
selfCloseEmpty: !config.get("XML_NO_EMPTY")
|
||||||
};
|
};
|
||||||
|
|
||||||
const XML_IGNORE_NAMESPACE = config.get("XML_IGNORE_NAMESPACE");
|
const XML_IGNORE_NAMESPACE = config.get("XML_IGNORE_NAMESPACE");
|
||||||
@@ -79,32 +79,29 @@ const XML_IGNORE_NAMESPACE = config.get("XML_IGNORE_NAMESPACE");
|
|||||||
if (XML_IGNORE_NAMESPACE) {
|
if (XML_IGNORE_NAMESPACE) {
|
||||||
libxmljs.Element.prototype.__find = libxmljs.Element.prototype.find;
|
libxmljs.Element.prototype.__find = libxmljs.Element.prototype.find;
|
||||||
libxmljs.Element.prototype.find = function(xpath, namespaces) {
|
libxmljs.Element.prototype.find = function(xpath, namespaces) {
|
||||||
const p = xpath.replace(/([^\/:]+:)?([^\/]+)(\/|$)/g, "$2$3").replace(/([a-zA-Z0-9_-]+)([^\/]*)(\/|$)/g, "*[local-name()=\"$1\"]$2$3");
|
const p = xpath
|
||||||
|
.replace(/([^/:]+:)?([^/]+)(\/|$)/g, "$2$3")
|
||||||
|
.replace(/([a-zA-Z0-9_-]+)([^/]*)(\/|$)/g, '*[local-name()="$1"]$2$3');
|
||||||
return libxmljs.Element.prototype.__find.call(this, p, namespaces);
|
return libxmljs.Element.prototype.__find.call(this, p, namespaces);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let warnings;
|
let warnings;
|
||||||
|
|
||||||
|
|
||||||
function parseBool(v) {
|
function parseBool(v) {
|
||||||
v = "" + v;
|
v = "" + v;
|
||||||
if (v === "true" || v === "TRUE" || v === "True" || v === "1")
|
if (v === "true" || v === "TRUE" || v === "True" || v === "1") return true;
|
||||||
return true;
|
|
||||||
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
||||||
return false;
|
return false;
|
||||||
else
|
else return null;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function event(xml) {
|
function event(xml) {
|
||||||
return xml.find("EventStruct/EventCode").map(e => e.text().trim());
|
return xml.find("EventStruct/EventCode").map(e => e.text().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function parameterInfoList(xml) {
|
function parameterInfoList(xml) {
|
||||||
return xml.find("ParameterInfoStruct").map(function(e) {
|
return xml.find("ParameterInfoStruct").map(e => {
|
||||||
let param = e.get("Name").text();
|
let param = e.get("Name").text();
|
||||||
let value = e.get("Writable").text();
|
let value = e.get("Writable").text();
|
||||||
let parsed = parseBool(value);
|
let parsed = parseBool(value);
|
||||||
@@ -121,9 +118,8 @@ function parameterInfoList(xml) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function parameterValueList(xml) {
|
function parameterValueList(xml) {
|
||||||
return xml.find("ParameterValueStruct").map(function(e) {
|
return xml.find("ParameterValueStruct").map(e => {
|
||||||
const valueElement = e.get("Value");
|
const valueElement = e.get("Value");
|
||||||
const valueType = valueElement.attr("type").value();
|
const valueType = valueElement.attr("type").value();
|
||||||
let param = e.get("Name").text();
|
let param = e.get("Name").text();
|
||||||
@@ -138,8 +134,7 @@ function parameterValueList(xml) {
|
|||||||
});
|
});
|
||||||
parsed = value;
|
parsed = value;
|
||||||
}
|
}
|
||||||
}
|
} else if (valueType === "xsd:int" || valueType === "xsd:unsignedInt") {
|
||||||
else if (valueType === "xsd:int" || valueType === "xsd:unsignedInt") {
|
|
||||||
parsed = parseInt(value);
|
parsed = parseInt(value);
|
||||||
if (isNaN(parsed)) {
|
if (isNaN(parsed)) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
@@ -148,8 +143,7 @@ function parameterValueList(xml) {
|
|||||||
});
|
});
|
||||||
parsed = value;
|
parsed = value;
|
||||||
}
|
}
|
||||||
}
|
} else if (valueType === "xsd:dateTime") {
|
||||||
else if (valueType === "xsd:dateTime") {
|
|
||||||
parsed = Date.parse(value);
|
parsed = Date.parse(value);
|
||||||
if (isNaN(parsed)) {
|
if (isNaN(parsed)) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
@@ -160,18 +154,16 @@ function parameterValueList(xml) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [param, parsed, valueType];
|
return [param, parsed, valueType];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetParameterNames(xml, methodRequest) {
|
function GetParameterNames(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:GetParameterNames");
|
const el = xml.node("cwmp:GetParameterNames");
|
||||||
el.node("ParameterPath").text(methodRequest.parameterPath);
|
el.node("ParameterPath").text(methodRequest.parameterPath);
|
||||||
el.node("NextLevel").text(+methodRequest.nextLevel);
|
el.node("NextLevel").text(+methodRequest.nextLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetParameterNamesResponse(xml) {
|
function GetParameterNamesResponse(xml) {
|
||||||
return {
|
return {
|
||||||
name: "GetParameterNamesResponse",
|
name: "GetParameterNamesResponse",
|
||||||
@@ -179,18 +171,15 @@ function GetParameterNamesResponse(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetParameterValues(xml, methodRequest) {
|
function GetParameterValues(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:GetParameterValues").node("ParameterNames");
|
const el = xml.node("cwmp:GetParameterValues").node("ParameterNames");
|
||||||
el.attr({
|
el.attr({
|
||||||
"soap-enc:arrayType": `xsd:string[${methodRequest.parameterNames.length}]`
|
"soap-enc:arrayType": `xsd:string[${methodRequest.parameterNames.length}]`
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const p of methodRequest.parameterNames)
|
for (const p of methodRequest.parameterNames) el.node("string").text(p);
|
||||||
el.node("string").text(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetParameterValuesResponse(xml) {
|
function GetParameterValuesResponse(xml) {
|
||||||
return {
|
return {
|
||||||
name: "GetParameterValuesResponse",
|
name: "GetParameterValuesResponse",
|
||||||
@@ -198,34 +187,36 @@ function GetParameterValuesResponse(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function SetParameterValues(xml, methodRequest) {
|
function SetParameterValues(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:SetParameterValues");
|
const el = xml.node("cwmp:SetParameterValues");
|
||||||
const paramList = el.node("ParameterList");
|
const paramList = el.node("ParameterList");
|
||||||
paramList.attr({
|
paramList.attr({
|
||||||
"soap-enc:arrayType": `cwmp:ParameterValueStruct[${methodRequest.parameterList.length}]`
|
"soap-enc:arrayType": `cwmp:ParameterValueStruct[${
|
||||||
|
methodRequest.parameterList.length
|
||||||
|
}]`
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const p of methodRequest.parameterList) {
|
for (const p of methodRequest.parameterList) {
|
||||||
const pvs = paramList.node("ParameterValueStruct");
|
const pvs = paramList.node("ParameterValueStruct");
|
||||||
pvs.node("Name").text(p[0]);
|
pvs.node("Name").text(p[0]);
|
||||||
let val = p[1];
|
let val = p[1];
|
||||||
if (p[2] === 'xsd:dateTime' && typeof val === 'number') {
|
if (p[2] === "xsd:dateTime" && typeof val === "number") {
|
||||||
val = (new Date(val)).toISOString();
|
val = new Date(val).toISOString();
|
||||||
if (methodRequest.DATETIME_MILLISECONDS === false)
|
if (methodRequest.DATETIME_MILLISECONDS === false)
|
||||||
val = val.replace('.000', '');
|
val = val.replace(".000", "");
|
||||||
}
|
}
|
||||||
if (p[2] === 'xsd:boolean' && typeof val === 'boolean') {
|
if (p[2] === "xsd:boolean" && typeof val === "boolean")
|
||||||
if (methodRequest.BOOLEAN_LITERAL === false)
|
if (methodRequest.BOOLEAN_LITERAL === false) val = +val;
|
||||||
val = +val;
|
|
||||||
}
|
pvs
|
||||||
pvs.node("Value").attr({"xsi:type": p[2]}).text(val);
|
.node("Value")
|
||||||
|
.attr({ "xsi:type": p[2] })
|
||||||
|
.text(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function SetParameterValuesResponse(xml) {
|
function SetParameterValuesResponse(xml) {
|
||||||
return {
|
return {
|
||||||
name: "SetParameterValuesResponse",
|
name: "SetParameterValuesResponse",
|
||||||
@@ -233,14 +224,12 @@ function SetParameterValuesResponse(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function AddObject(xml, methodRequest) {
|
function AddObject(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:AddObject");
|
const el = xml.node("cwmp:AddObject");
|
||||||
el.node("ObjectName").text(methodRequest.objectName);
|
el.node("ObjectName").text(methodRequest.objectName);
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function AddObjectResponse(xml) {
|
function AddObjectResponse(xml) {
|
||||||
return {
|
return {
|
||||||
name: "AddObjectResponse",
|
name: "AddObjectResponse",
|
||||||
@@ -249,14 +238,12 @@ function AddObjectResponse(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function DeleteObject(xml, methodRequest) {
|
function DeleteObject(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:DeleteObject");
|
const el = xml.node("cwmp:DeleteObject");
|
||||||
el.node("ObjectName").text(methodRequest.objectName);
|
el.node("ObjectName").text(methodRequest.objectName);
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function DeleteObjectResponse(xml) {
|
function DeleteObjectResponse(xml) {
|
||||||
return {
|
return {
|
||||||
name: "DeleteObjectResponse",
|
name: "DeleteObjectResponse",
|
||||||
@@ -264,32 +251,27 @@ function DeleteObjectResponse(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Reboot(xml, methodRequest) {
|
function Reboot(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:Reboot");
|
const el = xml.node("cwmp:Reboot");
|
||||||
el.node("CommandKey").text(methodRequest.commandKey || "");
|
el.node("CommandKey").text(methodRequest.commandKey || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RebootResponse() {
|
||||||
function RebootResponse(xml) {
|
|
||||||
return {
|
return {
|
||||||
name: "RebootResponse"
|
name: "RebootResponse"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FactoryReset(xml) {
|
||||||
function FactoryReset(xml, methodRequest) {
|
|
||||||
xml.node("cwmp:FactoryReset");
|
xml.node("cwmp:FactoryReset");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FactoryResetResponse() {
|
||||||
function FactoryResetResponse(xml, methodRequest) {
|
|
||||||
return {
|
return {
|
||||||
name: "FactoryResetResponse"
|
name: "FactoryResetResponse"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Download(xml, methodRequest) {
|
function Download(xml, methodRequest) {
|
||||||
const el = xml.node("cwmp:Download");
|
const el = xml.node("cwmp:Download");
|
||||||
el.node("CommandKey").text(methodRequest.commandKey || "");
|
el.node("CommandKey").text(methodRequest.commandKey || "");
|
||||||
@@ -304,7 +286,6 @@ function Download(xml, methodRequest) {
|
|||||||
el.node("FailureURL").text(methodRequest.failureUrl || "");
|
el.node("FailureURL").text(methodRequest.failureUrl || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function DownloadResponse(xml) {
|
function DownloadResponse(xml) {
|
||||||
const res = {
|
const res = {
|
||||||
name: "DownloadResponse",
|
name: "DownloadResponse",
|
||||||
@@ -319,44 +300,41 @@ function DownloadResponse(xml) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Inform(xml) {
|
function Inform(xml) {
|
||||||
return {
|
return {
|
||||||
name: "Inform",
|
name: "Inform",
|
||||||
parameterList: parameterValueList(xml.get("ParameterList")),
|
parameterList: parameterValueList(xml.get("ParameterList")),
|
||||||
deviceId: {
|
deviceId: {
|
||||||
"Manufacturer": xml.get("DeviceId/Manufacturer").text(),
|
Manufacturer: xml.get("DeviceId/Manufacturer").text(),
|
||||||
"OUI": xml.get("DeviceId/OUI").text(),
|
OUI: xml.get("DeviceId/OUI").text(),
|
||||||
"ProductClass": xml.get("DeviceId/ProductClass").text(),
|
ProductClass: xml.get("DeviceId/ProductClass").text(),
|
||||||
"SerialNumber": xml.get("DeviceId/SerialNumber").text()
|
SerialNumber: xml.get("DeviceId/SerialNumber").text()
|
||||||
},
|
},
|
||||||
event: event(xml.get("Event")),
|
event: event(xml.get("Event")),
|
||||||
retryCount: parseInt(xml.get("RetryCount").text())
|
retryCount: parseInt(xml.get("RetryCount").text())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function InformResponse(xml) {
|
||||||
function InformResponse(xml, methodResponse) {
|
xml
|
||||||
xml.node("cwmp:InformResponse").node("MaxEnvelopes").text(1);
|
.node("cwmp:InformResponse")
|
||||||
|
.node("MaxEnvelopes")
|
||||||
|
.text(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GetRPCMethods() {
|
||||||
function GetRPCMethods(xml) {
|
return { name: "GetRPCMethods" };
|
||||||
return {name: "GetRPCMethods"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetRPCMethodsResponse(xml, methodResponse) {
|
function GetRPCMethodsResponse(xml, methodResponse) {
|
||||||
const el = xml.node("cwmp:GetRPCMethodsResponse").node("MethodList");
|
const el = xml.node("cwmp:GetRPCMethodsResponse").node("MethodList");
|
||||||
el.attr({
|
el.attr({
|
||||||
"soap-enc:arrayType": `xsd:string[${methodResponse.methodList.length}]`
|
"soap-enc:arrayType": `xsd:string[${methodResponse.methodList.length}]`
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const m of methodResponse.methodList)
|
for (const m of methodResponse.methodList) el.node("string").text(m);
|
||||||
el.node("string").text(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function TransferComplete(xml) {
|
function TransferComplete(xml) {
|
||||||
return {
|
return {
|
||||||
name: "TransferComplete",
|
name: "TransferComplete",
|
||||||
@@ -367,12 +345,10 @@ function TransferComplete(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TransferCompleteResponse(xml) {
|
||||||
function TransferCompleteResponse(xml, methodResponse) {
|
|
||||||
xml.node("cwmp:TransferCompleteResponse").text("");
|
xml.node("cwmp:TransferCompleteResponse").text("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function RequestDownload(xml) {
|
function RequestDownload(xml) {
|
||||||
return {
|
return {
|
||||||
name: "RequestDownload",
|
name: "RequestDownload",
|
||||||
@@ -380,12 +356,10 @@ function RequestDownload(xml) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RequestDownloadResponse(xml) {
|
||||||
function RequestDownloadResponse(xml, methodResponse) {
|
|
||||||
xml.node("cwmp:RequestDownloadResponse").text("");
|
xml.node("cwmp:RequestDownloadResponse").text("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function faultStruct(xml) {
|
function faultStruct(xml) {
|
||||||
const f = {
|
const f = {
|
||||||
faultCode: xml.get("FaultCode").text(),
|
faultCode: xml.get("FaultCode").text(),
|
||||||
@@ -393,8 +367,7 @@ function faultStruct(xml) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const e of xml.find("SetParameterValuesFault")) {
|
for (const e of xml.find("SetParameterValuesFault")) {
|
||||||
if (!f.setParameterValuesFault)
|
if (!f.setParameterValuesFault) f.setParameterValuesFault = [];
|
||||||
f.setParameterValuesFault = [];
|
|
||||||
|
|
||||||
f.setParameterValuesFault.push({
|
f.setParameterValuesFault.push({
|
||||||
parameterName: e.get("ParameterName").text(),
|
parameterName: e.get("ParameterName").text(),
|
||||||
@@ -406,7 +379,6 @@ function faultStruct(xml) {
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function fault(xml, cwmpVersion) {
|
function fault(xml, cwmpVersion) {
|
||||||
return {
|
return {
|
||||||
faultCode: xml.get("faultcode").text(),
|
faultCode: xml.get("faultcode").text(),
|
||||||
@@ -415,7 +387,6 @@ function fault(xml, cwmpVersion) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function request(data, cwmpVersion, warn) {
|
function request(data, cwmpVersion, warn) {
|
||||||
warnings = warn;
|
warnings = warn;
|
||||||
|
|
||||||
@@ -423,19 +394,24 @@ function request(data, cwmpVersion, warn) {
|
|||||||
cwmpVersion: cwmpVersion
|
cwmpVersion: cwmpVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data.length)
|
if (!data.length) return rpc;
|
||||||
return rpc;
|
|
||||||
|
|
||||||
const xml = libxmljs.parseXml(data, LIBXMLJS_PARSE_OPTIONS);
|
const xml = libxmljs.parseXml(data, LIBXMLJS_PARSE_OPTIONS);
|
||||||
|
|
||||||
let methodElement;
|
let methodElement;
|
||||||
|
|
||||||
if (rpc.cwmpVersion) {
|
if (rpc.cwmpVersion) {
|
||||||
methodElement = xml.get("/soap-env:Envelope/soap-env:Body/cwmp:*", NAMESPACES[rpc.cwmpVersion]);
|
methodElement = xml.get(
|
||||||
}
|
"/soap-env:Envelope/soap-env:Body/cwmp:*",
|
||||||
else {
|
NAMESPACES[rpc.cwmpVersion]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
// cwmpVersion not passed, thus it's an inform request
|
// cwmpVersion not passed, thus it's an inform request
|
||||||
methodElement = xml.get("/soap-env:Envelope/soap-env:Body/*", NAMESPACES["1.0"]);
|
methodElement = xml.get(
|
||||||
|
"/soap-env:Envelope/soap-env:Body/*",
|
||||||
|
NAMESPACES["1.0"]
|
||||||
|
);
|
||||||
|
let timeoutElement;
|
||||||
switch (methodElement.namespace().href()) {
|
switch (methodElement.namespace().href()) {
|
||||||
case "urn:dslforum-org:cwmp-1-0":
|
case "urn:dslforum-org:cwmp-1-0":
|
||||||
rpc.cwmpVersion = "1.0";
|
rpc.cwmpVersion = "1.0";
|
||||||
@@ -444,12 +420,14 @@ function request(data, cwmpVersion, warn) {
|
|||||||
rpc.cwmpVersion = "1.1";
|
rpc.cwmpVersion = "1.1";
|
||||||
break;
|
break;
|
||||||
case "urn:dslforum-org:cwmp-1-2":
|
case "urn:dslforum-org:cwmp-1-2":
|
||||||
const timeoutElement = xml.get("/soap-env:Envelope/soap-env:Header/cwmp:sessionTimeout", NAMESPACES["1.2"]);
|
timeoutElement = xml.get(
|
||||||
|
"/soap-env:Envelope/soap-env:Header/cwmp:sessionTimeout",
|
||||||
|
NAMESPACES["1.2"]
|
||||||
|
);
|
||||||
if (timeoutElement) {
|
if (timeoutElement) {
|
||||||
rpc.sessionTimeout = parseInt(timeoutElement.text());
|
rpc.sessionTimeout = parseInt(timeoutElement.text());
|
||||||
rpc.cwmpVersion = "1.3";
|
rpc.cwmpVersion = "1.3";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
rpc.cwmpVersion = "1.2";
|
rpc.cwmpVersion = "1.2";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -461,17 +439,24 @@ function request(data, cwmpVersion, warn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!rpc.sessionTimeout) {
|
if (!rpc.sessionTimeout) {
|
||||||
const timeoutElement = xml.get("/soap-env:Envelope/soap-env:Header/cwmp:sessionTimeout", NAMESPACES[rpc.cwmpVersion]);
|
timeoutElement = xml.get(
|
||||||
if (timeoutElement)
|
"/soap-env:Envelope/soap-env:Header/cwmp:sessionTimeout",
|
||||||
rpc.sessionTimeout = parseInt(timeoutElement.text());
|
NAMESPACES[rpc.cwmpVersion]
|
||||||
|
);
|
||||||
|
if (timeoutElement) rpc.sessionTimeout = parseInt(timeoutElement.text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let idElement = xml.get("/soap-env:Envelope/soap-env:Header/cwmp:ID", NAMESPACES[rpc.cwmpVersion]);
|
let idElement = xml.get(
|
||||||
if (idElement)
|
"/soap-env:Envelope/soap-env:Header/cwmp:ID",
|
||||||
rpc.id = idElement.text();
|
NAMESPACES[rpc.cwmpVersion]
|
||||||
|
);
|
||||||
|
if (idElement) rpc.id = idElement.text();
|
||||||
|
|
||||||
if (methodElement && !(XML_IGNORE_NAMESPACE && methodElement.name() === "Fault")) {
|
if (
|
||||||
|
methodElement &&
|
||||||
|
!(XML_IGNORE_NAMESPACE && methodElement.name() === "Fault")
|
||||||
|
) {
|
||||||
switch (methodElement.name()) {
|
switch (methodElement.name()) {
|
||||||
case "Inform":
|
case "Inform":
|
||||||
rpc.cpeRequest = Inform(methodElement);
|
rpc.cpeRequest = Inform(methodElement);
|
||||||
@@ -512,16 +497,17 @@ function request(data, cwmpVersion, warn) {
|
|||||||
default:
|
default:
|
||||||
throw new Error(`8000 Method not supported ${methodElement.name()}`);
|
throw new Error(`8000 Method not supported ${methodElement.name()}`);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
const faultElement = xml.get(
|
||||||
const faultElement = xml.get("/soap-env:Envelope/soap-env:Body/soap-env:Fault", NAMESPACES[rpc.cwmpVersion]);
|
"/soap-env:Envelope/soap-env:Body/soap-env:Fault",
|
||||||
|
NAMESPACES[rpc.cwmpVersion]
|
||||||
|
);
|
||||||
rpc.cpeFault = fault(faultElement, rpc.cwmpVersion);
|
rpc.cpeFault = fault(faultElement, rpc.cwmpVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rpc;
|
return rpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function createSoapEnv(cwmpVersion) {
|
function createSoapEnv(cwmpVersion) {
|
||||||
const xml = libxmljs.Document();
|
const xml = libxmljs.Document();
|
||||||
const env = xml.node("soap-env:Envelope");
|
const env = xml.node("soap-env:Envelope");
|
||||||
@@ -532,21 +518,22 @@ function createSoapEnv(cwmpVersion) {
|
|||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function response(rpc) {
|
function response(rpc) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"Server": SERVER_NAME,
|
Server: SERVER_NAME,
|
||||||
"SOAPServer": SERVER_NAME
|
SOAPServer: SERVER_NAME
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!rpc)
|
if (!rpc) return { code: 204, headers: headers, data: new Buffer(0) };
|
||||||
return {code: 204, headers: headers, data: new Buffer(0)};
|
|
||||||
|
|
||||||
const env = createSoapEnv(rpc.cwmpVersion);
|
const env = createSoapEnv(rpc.cwmpVersion);
|
||||||
const header = env.node("soap-env:Header");
|
const header = env.node("soap-env:Header");
|
||||||
header.node("cwmp:ID").attr({
|
header
|
||||||
"soap-env:mustUnderstand": 1
|
.node("cwmp:ID")
|
||||||
}).text(rpc.id);
|
.attr({
|
||||||
|
"soap-env:mustUnderstand": 1
|
||||||
|
})
|
||||||
|
.text(rpc.id);
|
||||||
|
|
||||||
const body = env.node("soap-env:Body");
|
const body = env.node("soap-env:Body");
|
||||||
|
|
||||||
@@ -567,8 +554,7 @@ function response(rpc) {
|
|||||||
default:
|
default:
|
||||||
throw new Error(`Unknown method response type ${rpc.acsResponse.name}`);
|
throw new Error(`Unknown method response type ${rpc.acsResponse.name}`);
|
||||||
}
|
}
|
||||||
}
|
} else if (rpc.acsRequest) {
|
||||||
else if (rpc.acsRequest) {
|
|
||||||
switch (rpc.acsRequest.name) {
|
switch (rpc.acsRequest.name) {
|
||||||
case "GetParameterNames":
|
case "GetParameterNames":
|
||||||
GetParameterNames(body, rpc.acsRequest);
|
GetParameterNames(body, rpc.acsRequest);
|
||||||
@@ -599,7 +585,7 @@ function response(rpc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
headers["Content-Type"] = "text/xml; charset=\"utf-8\"";
|
headers["Content-Type"] = 'text/xml; charset="utf-8"';
|
||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
@@ -607,6 +593,5 @@ function response(rpc) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.request = request;
|
exports.request = request;
|
||||||
exports.response = response;
|
exports.response = response;
|
||||||
+65
-96
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2017 Zaid Abdulla
|
* Copyright 2013-2018 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -20,18 +20,21 @@
|
|||||||
|
|
||||||
const NONEXISTENT = new Object();
|
const NONEXISTENT = new Object();
|
||||||
|
|
||||||
|
|
||||||
function compareEquality(a, b) {
|
function compareEquality(a, b) {
|
||||||
var t = typeof a;
|
var t = typeof a;
|
||||||
if (t === 'number' || (a == null) || t === 'boolean' || t === 'string' || t === 'symbol')
|
if (
|
||||||
|
t === "number" ||
|
||||||
|
a == null ||
|
||||||
|
t === "boolean" ||
|
||||||
|
t === "string" ||
|
||||||
|
t === "symbol"
|
||||||
|
)
|
||||||
return a === b;
|
return a === b;
|
||||||
|
|
||||||
return JSON.stringify(a) === JSON.stringify(b);
|
return JSON.stringify(a) === JSON.stringify(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VersionedMap {
|
class VersionedMap {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._sizeDiff = [0];
|
this._sizeDiff = [0];
|
||||||
this._revision = 0;
|
this._revision = 0;
|
||||||
@@ -39,71 +42,59 @@ class VersionedMap {
|
|||||||
this.dirty = 0;
|
this.dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get size() {
|
get size() {
|
||||||
return this.map.size + this._sizeDiff[this.revision];
|
return this.map.size + this._sizeDiff[this.revision];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get revision() {
|
get revision() {
|
||||||
return this._revision;
|
return this._revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
set revision(rev) {
|
set revision(rev) {
|
||||||
for (let i = this._sizeDiff.length; i <= rev; ++ i)
|
for (let i = this._sizeDiff.length; i <= rev; ++i)
|
||||||
this._sizeDiff[i] = this._sizeDiff[i - 1];
|
this._sizeDiff[i] = this._sizeDiff[i - 1];
|
||||||
|
|
||||||
this._revision = rev;
|
this._revision = rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get(key, rev) {
|
get(key, rev) {
|
||||||
if (rev == null)
|
if (rev == null) rev = this._revision;
|
||||||
rev = this._revision
|
|
||||||
|
|
||||||
var v = this.map.get(key);
|
var v = this.map.get(key);
|
||||||
if (!v)
|
if (!v) return;
|
||||||
return;
|
|
||||||
|
|
||||||
for (let i = Math.min(v.length - 1, rev); i >= 0; -- i)
|
for (let i = Math.min(v.length - 1, rev); i >= 0; --i) {
|
||||||
if (i in v)
|
if (i in v) {
|
||||||
if (v[i] === NONEXISTENT)
|
if (v[i] === NONEXISTENT) return undefined;
|
||||||
return undefined;
|
else return v[i];
|
||||||
else
|
}
|
||||||
return v[i];
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
has(key, rev) {
|
has(key, rev) {
|
||||||
if (rev == null)
|
if (rev == null) rev = this._revision;
|
||||||
rev = this._revision
|
|
||||||
|
|
||||||
var v = this.map.get(key);
|
var v = this.map.get(key);
|
||||||
|
|
||||||
if (v == null)
|
if (v == null) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
for (let i = Math.min(v.length - 1, rev); i >= 0; -- i)
|
for (let i = Math.min(v.length - 1, rev); i >= 0; --i) {
|
||||||
if (i in v)
|
if (i in v) {
|
||||||
if (v[i] === NONEXISTENT)
|
if (v[i] === NONEXISTENT) return false;
|
||||||
return false;
|
else return true;
|
||||||
else
|
}
|
||||||
return true;
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
set(key, value, rev) {
|
set(key, value, rev) {
|
||||||
if (rev == null)
|
if (rev == null) rev = this._revision;
|
||||||
rev = this._revision;
|
|
||||||
|
|
||||||
var v = this.map.get(key);
|
var v = this.map.get(key);
|
||||||
if (!v) {
|
if (!v) {
|
||||||
this.dirty |= 1 << rev;
|
this.dirty |= 1 << rev;
|
||||||
for (let i = 0; i < rev; ++ i)
|
for (let i = 0; i < rev; ++i) this._sizeDiff[i] -= 1;
|
||||||
this._sizeDiff[i] -= 1;
|
|
||||||
|
|
||||||
v = [];
|
v = [];
|
||||||
v[rev] = value;
|
v[rev] = value;
|
||||||
@@ -114,71 +105,59 @@ class VersionedMap {
|
|||||||
var old = v[Math.min(rev, v.length - 1)];
|
var old = v[Math.min(rev, v.length - 1)];
|
||||||
|
|
||||||
if (rev < v.length - 1) {
|
if (rev < v.length - 1) {
|
||||||
if (compareEquality(value, old))
|
if (compareEquality(value, old)) return;
|
||||||
return
|
else throw new Error("Cannot modify old revisions");
|
||||||
else
|
|
||||||
throw new Error('Cannot modify old revisions');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dirty |= 1 << rev;
|
this.dirty |= 1 << rev;
|
||||||
if (old === NONEXISTENT)
|
if (old === NONEXISTENT) ++this._sizeDiff[rev];
|
||||||
++ this._sizeDiff[rev];
|
|
||||||
|
|
||||||
v[rev] = value;
|
v[rev] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
delete(key, rev) {
|
delete(key, rev) {
|
||||||
if (rev == null)
|
if (rev == null) rev = this._revision;
|
||||||
rev = this._revision;
|
|
||||||
|
|
||||||
var v = this.map.get(key);
|
var v = this.map.get(key);
|
||||||
if (!v)
|
if (!v) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if (rev < v.length - 1)
|
if (rev < v.length - 1) throw new Error("Cannot modify old revisions");
|
||||||
throw new Error('Cannot modify old revisions');
|
|
||||||
|
|
||||||
var old = v[v.length - 1];
|
var old = v[v.length - 1];
|
||||||
if (old === NONEXISTENT)
|
if (old === NONEXISTENT) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
this.dirty |= 1 << rev;
|
this.dirty |= 1 << rev;
|
||||||
-- this._sizeDiff[rev];
|
--this._sizeDiff[rev];
|
||||||
v[rev] = NONEXISTENT;
|
v[rev] = NONEXISTENT;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getRevisions(key) {
|
getRevisions(key) {
|
||||||
var v = this.map.get(key);
|
var v = this.map.get(key);
|
||||||
if (!v)
|
if (!v) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var res = {};
|
var res = {};
|
||||||
|
|
||||||
for (let i in v)
|
for (let i in v) {
|
||||||
if (v[i] === NONEXISTENT)
|
if (v[i] === NONEXISTENT) res.delete |= 1 << i;
|
||||||
res.delete |= 1 << i;
|
else res[i] = v[i];
|
||||||
else
|
}
|
||||||
res[i] = v[i];
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setRevisions(key, revisions) {
|
setRevisions(key, revisions) {
|
||||||
var del = 0;
|
var del = 0;
|
||||||
var rev = [];
|
var rev = [];
|
||||||
var minKey = 999;
|
var minKey = 999;
|
||||||
for (let k in revisions) {
|
for (let k in revisions) {
|
||||||
if (k === 'delete')
|
if (k === "delete") {
|
||||||
del = revisions[k];
|
del = revisions[k];
|
||||||
else {
|
} else {
|
||||||
let r = parseInt(k);
|
let r = parseInt(k);
|
||||||
minKey = Math.min(minKey, r);
|
minKey = Math.min(minKey, r);
|
||||||
for (let i = this._sizeDiff.length; i <= r; ++ i)
|
for (let i = this._sizeDiff.length; i <= r; ++i)
|
||||||
this._sizeDiff[i] = this._sizeDiff[i - 1];
|
this._sizeDiff[i] = this._sizeDiff[i - 1];
|
||||||
|
|
||||||
this.dirty |= 1 << r;
|
this.dirty |= 1 << r;
|
||||||
@@ -186,63 +165,57 @@ class VersionedMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < minKey; ++ i)
|
for (let i = 0; i < minKey; ++i) --this._sizeDiff[i];
|
||||||
-- this._sizeDiff[i];
|
|
||||||
|
|
||||||
this.dirty |= del;
|
this.dirty |= del;
|
||||||
|
|
||||||
for (let i = 0; del > 0; del >>= 1, ++ i)
|
for (let i = 0; del > 0; del >>= 1, ++i) if (del & 1) rev[i] = NONEXISTENT;
|
||||||
if (del & 1)
|
|
||||||
rev[i] = NONEXISTENT;
|
|
||||||
|
|
||||||
this.map.set(key, rev);
|
this.map.set(key, rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getDiff(key) {
|
getDiff(key) {
|
||||||
var revisions = this.map.get(key);
|
var revisions = this.map.get(key);
|
||||||
if (!revisions)
|
if (!revisions) return [];
|
||||||
return [];
|
|
||||||
|
|
||||||
var current = revisions[revisions.length - 1];
|
var current = revisions[revisions.length - 1];
|
||||||
if (current === NONEXISTENT) {
|
if (current === NONEXISTENT) {
|
||||||
if (0 in revisions)
|
if (0 in revisions) return [key, revisions[0]];
|
||||||
return [key, revisions[0]];
|
else return [key];
|
||||||
else
|
} else if (0 in revisions) {
|
||||||
return [key];
|
|
||||||
} else if (0 in revisions)
|
|
||||||
return [key, revisions[0], current];
|
return [key, revisions[0], current];
|
||||||
else
|
} else {
|
||||||
|
// TODO don't use sparse arrays?
|
||||||
|
// eslint-disable-next-line no-sparse-arrays
|
||||||
return [key, , current];
|
return [key, , current];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
*diff() {
|
*diff() {
|
||||||
for (let pair of this.map) {
|
for (let pair of this.map) {
|
||||||
let current = pair[1][pair[1].length - 1];
|
let current = pair[1][pair[1].length - 1];
|
||||||
if (current === NONEXISTENT) {
|
if (current === NONEXISTENT) {
|
||||||
if (0 in pair[1])
|
if (0 in pair[1]) yield [pair[0], pair[1][0]];
|
||||||
yield [pair[0], pair[1][0]];
|
else yield [pair[0]];
|
||||||
else
|
} else if (0 in pair[1]) {
|
||||||
yield [pair[0]];
|
|
||||||
} else if (0 in pair[1])
|
|
||||||
yield [pair[0], pair[1][0], current];
|
yield [pair[0], pair[1][0], current];
|
||||||
else
|
} else {
|
||||||
|
// TODO don't use sparse arrays?
|
||||||
|
// eslint-disable-next-line no-sparse-arrays
|
||||||
yield [pair[0], , current];
|
yield [pair[0], , current];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
collapse(revision) {
|
collapse(revision) {
|
||||||
if (this._sizeDiff.length <= revision)
|
if (this._sizeDiff.length <= revision) return;
|
||||||
return;
|
|
||||||
|
|
||||||
this._sizeDiff[revision] = this._sizeDiff[this._sizeDiff.length - 1];
|
this._sizeDiff[revision] = this._sizeDiff[this._sizeDiff.length - 1];
|
||||||
this._sizeDiff.splice(revision + 1, this._sizeDiff.length);
|
this._sizeDiff.splice(revision + 1, this._sizeDiff.length);
|
||||||
|
|
||||||
let d = this.dirty >> revision;
|
let d = this.dirty >> revision;
|
||||||
this.dirty = this.dirty ^ (d << revision);
|
this.dirty = this.dirty ^ (d << revision);
|
||||||
this.dirty |= (+!!d) << revision;
|
this.dirty |= +!!d << revision;
|
||||||
|
|
||||||
for (let pair of this.map) {
|
for (let pair of this.map) {
|
||||||
let k = pair[0];
|
let k = pair[0];
|
||||||
@@ -250,18 +223,14 @@ class VersionedMap {
|
|||||||
let last = v[v.length - 1];
|
let last = v[v.length - 1];
|
||||||
if (last === NONEXISTENT) {
|
if (last === NONEXISTENT) {
|
||||||
v.splice(revision, v.length);
|
v.splice(revision, v.length);
|
||||||
if (v.some(function(val) { return true; }))
|
if (v.some(() => true)) v[revision] = NONEXISTENT;
|
||||||
v[revision] = NONEXISTENT;
|
else this.map.delete(k);
|
||||||
else
|
} else {
|
||||||
this.map.delete(k);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[revision] = last;
|
v[revision] = last;
|
||||||
v.splice(revision + 1, v.length);
|
v.splice(revision + 1, v.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = VersionedMap;
|
module.exports = VersionedMap;
|
||||||
+4
-1
@@ -28,6 +28,8 @@
|
|||||||
"redis": "^2.6.5"
|
"redis": "^2.6.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"eslint": "^5.4.0",
|
||||||
|
"prettier": "^1.14.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.2 <9.0"
|
"node": ">=6.9.2 <9.0"
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
"compile": "echo 'Compile step no longer required, skipping.'",
|
"compile": "echo 'Compile step no longer required, skipping.'",
|
||||||
"configure": "cp -n ./config/config-sample.json ./config/config.json",
|
"configure": "cp -n ./config/config-sample.json ./config/config.json",
|
||||||
"install": "npm run configure",
|
"install": "npm run configure",
|
||||||
"test": "node test"
|
"test": "node test",
|
||||||
|
"lint": "eslint --fix lib/*.js; prettier --write lib/*.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user