Update ESLint and Prettier

This commit is contained in:
Zaid Abdulla committed 2020-07-27 20:30:13 -07:00
1 parent 5a9e03db2c
commit ac980116bd
110 files changed
+2993 -2794

No files matched your search

+21 -17
View File
@@ -24,11 +24,11 @@ import * as cache from "./cache";
import {
getCurrentSnapshot,
getConfig,
getConfigExpression
getConfigExpression,
} from "./local-cache";
import {
httpConnectionRequest,
udpConnectionRequest
udpConnectionRequest,
} from "./connection-request";
import { Expression, Task } from "./types";
import { flattenDevice } from "./mongodb-functions";
@@ -36,7 +36,7 @@ import { evaluate } from "./common/expression";
export async function connectionRequest(
deviceId: string,
device?: {}
device?: Record<string, { value?: [boolean | number | string, string] }>
): Promise<void> {
if (!device) {
const res = await db.devicesCollection.findOne({ _id: deviceId });
@@ -137,7 +137,7 @@ export async function connectionRequest(
"FUNC",
"AUTH",
["PARAM", "username"],
["PARAM", "password"]
["PARAM", "password"],
] as Expression;
}
@@ -171,8 +171,12 @@ export async function connectionRequest(
}
}
export async function watchTask(deviceId, taskId, timeout): Promise<string> {
await new Promise(resolve => setTimeout(resolve, 500));
export async function watchTask(
deviceId: string,
taskId: string,
timeout: number
): Promise<string> {
await new Promise((resolve) => setTimeout(resolve, 500));
const task = await db.tasksCollection.findOne(
{ _id: taskId },
@@ -182,7 +186,7 @@ export async function watchTask(deviceId, taskId, timeout): Promise<string> {
const q = { _id: `${deviceId}:task_${taskId}` };
const fault = await db.faultsCollection.findOne(q, {
projection: { _id: 1 }
projection: { _id: 1 },
});
if (fault) return "fault";
@@ -194,7 +198,7 @@ export async function watchTask(deviceId, taskId, timeout): Promise<string> {
function sanitizeTask(task): void {
task.timestamp = new Date(task.timestamp || Date.now());
if (task.expiry) {
if (common.typeOf(task.expiry) === common.DATE_TYPE || isNaN(task.expiry))
if (task.expiry instanceof Date || isNaN(task.expiry))
task.expiry = new Date(task.expiry);
else task.expiry = new Date(task.timestamp.getTime() + +task.expiry * 1000);
}
@@ -269,8 +273,8 @@ function sanitizeTask(task): void {
return task;
}
export async function insertTasks(tasks): Promise<Task[]> {
if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks];
export async function insertTasks(tasks: any[]): Promise<Task[]> {
if (tasks && !Array.isArray(tasks)) tasks = [tasks];
else if (!tasks || tasks.length === 0) return tasks || [];
for (const task of tasks) {
@@ -278,7 +282,7 @@ export async function insertTasks(tasks): Promise<Task[]> {
if (task.uniqueKey) {
await db.tasksCollection.deleteOne({
device: task.device,
uniqueKey: task.uniqueKey
uniqueKey: task.uniqueKey,
});
}
}
@@ -286,20 +290,20 @@ export async function insertTasks(tasks): Promise<Task[]> {
return tasks;
}
export async function deleteDevice(deviceId): Promise<void> {
export async function deleteDevice(deviceId: string): Promise<void> {
await Promise.all([
db.tasksCollection.deleteMany({ device: deviceId }),
db.devicesCollection.deleteOne({ _id: deviceId }),
db.faultsCollection.deleteMany({
_id: {
$regex: `^${common.escapeRegExp(deviceId)}\\:`
}
$regex: `^${common.escapeRegExp(deviceId)}\\:`,
},
}),
db.operationsCollection.deleteMany({
_id: {
$regex: `^${common.escapeRegExp(deviceId)}\\:`
}
$regex: `^${common.escapeRegExp(deviceId)}\\:`,
},
}),
cache.del(`${deviceId}_tasks_faults_operations`)
cache.del(`${deviceId}_tasks_faults_operations`),
]);
}
+15 -22
View File
@@ -19,11 +19,11 @@
import { createHash, randomBytes, pbkdf2 } from "crypto";
function parseHeaderFeilds(str: string): {} {
function parseHeaderFeilds(str: string): Record<string, string> {
const res = {};
const parts = str.split(",");
let part;
let part: string;
while ((part = parts.shift()) != null) {
const name = part.split("=", 1)[0];
if (name.length === part.length) {
@@ -52,7 +52,9 @@ function parseHeaderFeilds(str: string): {} {
return res;
}
export function parseAuthorizationHeader(authHeader): { method: string } {
export function parseAuthorizationHeader(
authHeader: string
): { method: string } {
authHeader = authHeader.trim();
const method = authHeader.split(" ", 1)[0];
const res = { method: method };
@@ -74,7 +76,9 @@ export function parseAuthorizationHeader(authHeader): { method: string } {
return res;
}
export function parseWwwAuthenticateHeader(authHeader): {} {
export function parseWwwAuthenticateHeader(
authHeader: string
): Record<string, string> {
authHeader = authHeader.trim();
const method = authHeader.split(" ", 1)[0];
const res = { method: method };
@@ -82,7 +86,7 @@ export function parseWwwAuthenticateHeader(authHeader): {} {
return res;
}
export function basic(username, password): string {
export function basic(username: string, password: string): string {
return "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
}
@@ -99,20 +103,12 @@ export function digest(
nc?: string | Buffer
): string {
const ha1 = createHash("md5");
ha1
.update(username)
.update(":")
.update(realm)
.update(":")
.update(password);
ha1.update(username).update(":").update(realm).update(":").update(password);
// TODO support "MD5-sess" algorithm directive
const ha1d = ha1.digest("hex");
const ha2 = createHash("md5");
ha2
.update(httpMethod)
.update(":")
.update(uri);
ha2.update(httpMethod).update(":").update(uri);
if (qop === "auth-int") {
const bodyHash = createHash("md5")
@@ -124,10 +120,7 @@ export function digest(
const ha2d = ha2.digest("hex");
const hash = createHash("md5");
hash
.update(ha1d)
.update(":")
.update(nonce);
hash.update(ha1d).update(":").update(nonce);
if (qop) {
hash
.update(":")
@@ -148,7 +141,7 @@ export function solveDigest(
uri: string | Buffer,
httpMethod: string | Buffer,
body: string | Buffer,
authHeader
authHeader: Record<string, string>
): string {
const cnonce = randomBytes(8).toString("hex");
const nc = "00000001";
@@ -185,7 +178,7 @@ export function solveDigest(
return authString;
}
export function generateSalt(length): Promise<string> {
export function generateSalt(length: number): Promise<string> {
return new Promise((resolve, reject) => {
randomBytes(length, (err, rand) => {
if (err) return void reject(err);
@@ -194,7 +187,7 @@ export function generateSalt(length): Promise<string> {
});
}
export function hashPassword(pass, salt): Promise<string> {
export function hashPassword(pass: string, salt: string): Promise<string> {
return new Promise((resolve, reject) => {
pbkdf2(pass, salt, 10000, 128, "sha512", (err, hash) => {
if (err) return void reject(err);
+13 -12
View File
@@ -29,7 +29,7 @@ let mongoTimeOffset = 0;
export async function connect(): Promise<void> {
const MONGODB_CONNECTION_URL = "" + config.get("MONGODB_CONNECTION_URL");
clientPromise = MongoClient.connect(MONGODB_CONNECTION_URL, {
useNewUrlParser: true
useNewUrlParser: true,
});
const db = (await clientPromise).db();
mongoCollection = db.collection("cache");
@@ -43,7 +43,7 @@ export async function disconnect(): Promise<void> {
if (clientPromise) await (await clientPromise).close();
}
export async function get(key): Promise<any> {
export async function get(key: string | string[]): Promise<any> {
const expire = new Date(Date.now() - mongoTimeOffset);
if (Array.isArray(key)) {
const res = await mongoCollection.find({ _id: { $in: key } }).toArray();
@@ -54,7 +54,7 @@ export async function get(key): Promise<any> {
});
const values = [];
res.forEach(r => {
res.forEach((r) => {
if (r["expire"] > expire) values[indices[r["_id"]]] = r["value"];
});
@@ -66,7 +66,7 @@ export async function get(key): Promise<any> {
}
}
export async function del(key): Promise<void> {
export async function del(key: string | string[]): Promise<void> {
if (Array.isArray(key))
await mongoCollection.deleteMany({ _id: { $in: key } });
else await mongoCollection.deleteOne({ _id: key });
@@ -85,7 +85,7 @@ export async function set(
);
}
export async function pop(key): Promise<any> {
export async function pop(key: string): Promise<any> {
const res = await mongoCollection.findOneAndDelete({ _id: key });
if (
@@ -98,16 +98,17 @@ export async function pop(key): Promise<any> {
return null;
}
export async function lock(lockName, ttl): Promise<Function> {
const token = Math.random()
.toString(36)
.slice(2);
export async function lock(
lockName: string,
ttl: number
): Promise<(extendTtl?: number) => Promise<void>> {
const token = Math.random().toString(36).slice(2);
async function unlockOrExtend(extendTtl): Promise<void> {
async function unlockOrExtend(extendTtl?: number): Promise<void> {
if (!extendTtl) {
const res = await mongoCollection.deleteOne({
_id: lockName,
value: token
value: token,
});
if (res["result"]["n"] !== 1) throw new Error("Lock expired");
} else {
@@ -131,7 +132,7 @@ export async function lock(lockName, ttl): Promise<Function> {
);
} catch (err) {
if (err && err.code === 11000) {
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise((resolve) => setTimeout(resolve, 200));
return lock(lockName, ttl);
}
}
+9 -5
View File
@@ -41,7 +41,7 @@ function restartWorker(worker, code, signal): void {
message: "Worker died",
pid: worker.process.pid,
exitCode: null,
signal: null
signal: null,
};
if (code != null) msg.exitCode = code;
@@ -57,7 +57,7 @@ function restartWorker(worker, code, signal): void {
min2 = 0,
min3 = 0;
crashes = crashes.filter(n => {
crashes = crashes.filter((n) => {
if (n > now - 60000) ++min1;
else if (n > now - 120000) ++min2;
else if (n > now - 180000) ++min3;
@@ -72,7 +72,7 @@ function restartWorker(worker, code, signal): void {
logger.error({
message: "Too many crashes, exiting",
pid: process.pid
pid: process.pid,
});
return;
}
@@ -89,7 +89,11 @@ function restartWorker(worker, code, signal): void {
}, respawnTimestamp - now);
}
export function start(workerCount, servicePort, serviceAddress): void {
export function start(
workerCount: number,
servicePort: number,
serviceAddress: string
): void {
cluster.on("listening", (worker, address) => {
if (
(address.addressType === 4 || address.addressType === 6) &&
@@ -100,7 +104,7 @@ export function start(workerCount, servicePort, serviceAddress): void {
message: "Worker listening",
pid: worker.process.pid,
address: address.address,
port: address.port
port: address.port,
});
}
});
+5 -15
View File
@@ -17,22 +17,12 @@
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
*/
export const UNDEFINED_TYPE = "[object Undefined]";
export const NULL_TYPE = "[object Null]";
export const BOOLEAN_TYPE = "[object Boolean]";
export const NUMBER_TYPE = "[object Number]";
export const STRING_TYPE = "[object String]";
export const ARRAY_TYPE = "[object Array]";
export const OBJECT_TYPE = "[object Object]";
export const REGEXP_TYPE = "[object RegExp]";
export const DATE_TYPE = "[object Date]";
export const typeOf = (obj): string => Object.prototype.toString.call(obj);
export function generateDeviceId(deviceIdStruct): string {
export function generateDeviceId(
deviceIdStruct: Record<string, string>
): string {
// Escapes everything except alphanumerics and underscore
function esc(str): string {
return str.replace(/[^A-Za-z0-9_]/g, chr => {
return str.replace(/[^A-Za-z0-9_]/g, (chr) => {
const buf = Buffer.from(chr, "utf8");
let rep = "";
for (const b of buf) rep += "%" + b.toString(16).toUpperCase();
@@ -54,6 +44,6 @@ export function generateDeviceId(deviceIdStruct): string {
}
// Source: http://stackoverflow.com/a/6969486
export function escapeRegExp(str): string {
export function escapeRegExp(str: string): string {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
}
+5 -5
View File
@@ -23,13 +23,13 @@ import { evaluate, or } from "./expression";
export default class Authorizer {
private permissionSets: PermissionSet[];
private validatorCache: WeakMap<
object,
any,
(mutationType, mutation, any) => boolean
>;
private hasAccessCache: Map<string, boolean>;
private getFilterCache: Map<string, Expression>;
public constructor(permissionSets) {
public constructor(permissionSets: PermissionSet[]) {
this.permissionSets = permissionSets;
this.validatorCache = new WeakMap();
this.hasAccessCache = new Map();
@@ -77,8 +77,8 @@ export default class Authorizer {
}
public getValidator(
resourceType,
resource
resourceType: string,
resource: unknown
): (mutationType: string, mutation?: any, args?: any) => boolean {
if (this.validatorCache.has(resource))
return this.validatorCache.get(resource);
@@ -108,7 +108,7 @@ export default class Authorizer {
mutation,
resourceType,
object: resource,
options: any
options: any,
};
const valueFunction = (paramName): any => {
+12 -12
View File
@@ -65,7 +65,7 @@ function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0): boolean {
export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
const ranges = new Map();
const likePatterns = new Map();
map(exp, e => {
map(exp, (e) => {
if (!Array.isArray(e)) return e;
if (e[0] === "LIKE" || e[0] === "NOT LIKE") {
if (
@@ -151,7 +151,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
if (to == null) vars.push(`${lhs}:${range[range.length - 1]}--`);
return vars.map(key => {
return vars.map((key) => {
let f = expressions.get(key);
if (!f) {
expressions.set(key, (f = expressions.size + 1));
@@ -172,7 +172,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
vars.push(`${lhs}:like:${JSON.stringify(p)}`);
}
return vars.map(key => {
return vars.map((key) => {
let f = expressions.get(key);
if (!f) expressions.set(key, (f = expressions.size + 1));
return f;
@@ -184,11 +184,11 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
}
const comparisonOperators: ComparisonOperators = {
"=": v => [v, true, v, true],
">": v => [v, false, null, null],
">=": v => [v, true, null, null],
"<": v => [null, null, v, false],
"<=": v => [null, null, v, true]
"=": (v) => [v, true, v, true],
">": (v) => [v, false, null, null],
">=": (v) => [v, true, null, null],
"<": (v) => [null, null, v, false],
"<=": (v) => [null, null, v, true],
};
function recursive(clause, negate): CNF {
@@ -211,7 +211,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
JSON.stringify(clause[1]),
...comparisonOperators["="](clause[2])
);
if (!negate) return vars.map(x => [0 - x]);
if (!negate) return vars.map((x) => [0 - x]);
else return [vars];
} else if (op === "IS NULL") {
const v = getVariable(JSON.stringify(clause[1]), "null");
@@ -227,7 +227,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
JSON.stringify(clause[1]),
...comparisonOperators[op](clause[2])
);
if (negate) return vars.map(x => [0 - x]);
if (negate) return vars.map((x) => [0 - x]);
else return [vars];
} else if (!Array.isArray(clause[1])) {
op = { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[op] || op;
@@ -235,7 +235,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
JSON.stringify(clause[2]),
...comparisonOperators[op](clause[1])
);
if (negate) return vars.map(x => [0 - x]);
if (negate) return vars.map((x) => [0 - x]);
else return [vars];
} else {
let lhs = JSON.stringify(clause[1]);
@@ -264,7 +264,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
clause[2],
clause[3]
);
if (negate) return vars.map(x => [0 - x]);
if (negate) return vars.map((x) => [0 - x]);
else return [vars];
} else {
const v = getVariable(JSON.stringify(clause), "");
+40 -43
View File
@@ -27,7 +27,7 @@ function interpretEscapes(str): string {
f: "\f",
n: "\n",
r: "\r",
t: "\t"
t: "\t",
};
return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => {
const type = escape.charAt(0);
@@ -40,7 +40,10 @@ function interpretEscapes(str): string {
});
}
export function map(exp, callback): Expression {
export function map(
exp: Expression,
callback: (exp: Expression) => Expression
): Expression {
if (!Array.isArray(exp)) return callback(exp);
let clone;
@@ -55,7 +58,10 @@ export function map(exp, callback): Expression {
return callback(clone || exp);
}
export async function mapAsync(exp, callback): Promise<Expression> {
export async function mapAsync(
exp: Expression,
callback: (exp: Expression) => Promise<Expression>
): Promise<Expression> {
if (!Array.isArray(exp)) return callback(exp);
let clone;
@@ -70,7 +76,7 @@ export async function mapAsync(exp, callback): Promise<Expression> {
return callback(clone || exp);
}
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<unknown> {
return parsimmon.seqMap(
nextParser,
parsimmon.seq(operatorsParser, nextParser).many(),
@@ -86,7 +92,7 @@ function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
}
const lang = parsimmon.createLanguage({
ComparisonOperator: function() {
ComparisonOperator: function () {
return parsimmon
.alt(
parsimmon.string(">="),
@@ -98,13 +104,10 @@ const lang = parsimmon.createLanguage({
)
.skip(parsimmon.optWhitespace);
},
LikeOperator: function() {
LikeOperator: function () {
return parsimmon
.alt(
parsimmon
.regexp(/like/i)
.result("LIKE")
.desc("LIKE"),
parsimmon.regexp(/like/i).result("LIKE").desc("LIKE"),
parsimmon
.regexp(/not\s+like/i)
.result("NOT LIKE")
@@ -113,7 +116,7 @@ const lang = parsimmon.createLanguage({
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
.skip(parsimmon.optWhitespace);
},
IsNullOperator: function() {
IsNullOperator: function () {
return parsimmon
.alt(
parsimmon
@@ -128,7 +131,7 @@ const lang = parsimmon.createLanguage({
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
.skip(parsimmon.optWhitespace);
},
NotOperator: function() {
NotOperator: function () {
return parsimmon
.regexp(/not/i)
.result("NOT")
@@ -136,7 +139,7 @@ const lang = parsimmon.createLanguage({
.skip(parsimmon.optWhitespace)
.desc("NOT");
},
AndOperator: function() {
AndOperator: function () {
return parsimmon
.regexp(/and/i)
.result("AND")
@@ -144,7 +147,7 @@ const lang = parsimmon.createLanguage({
.skip(parsimmon.optWhitespace)
.desc("AND");
},
OrOperator: function() {
OrOperator: function () {
return parsimmon
.regexp(/or/i)
.result("OR")
@@ -152,7 +155,7 @@ const lang = parsimmon.createLanguage({
.skip(parsimmon.optWhitespace)
.desc("OR");
},
Parameter: function(r) {
Parameter: function (r) {
return parsimmon
.alt(
parsimmon.regexp(/[a-zA-Z0-9_.*-]+/),
@@ -162,48 +165,42 @@ const lang = parsimmon.createLanguage({
)
)
.atLeast(1)
.map(x => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]])
.map((x) => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]])
.skip(parsimmon.optWhitespace)
.desc("parameter");
},
StringValueSql: function() {
StringValueSql: function () {
return parsimmon
.regexp(/'([^']*)'/, 1)
.atLeast(1)
.skip(parsimmon.optWhitespace)
.map(s => s.join("'"))
.map((s) => s.join("'"))
.desc("string");
},
StringValueJs: function() {
StringValueJs: function () {
return parsimmon
.regexp(/"((?:\\.|.)*?)"/, 1)
.skip(parsimmon.optWhitespace)
.map(interpretEscapes)
.desc("string");
},
NumberValue: function() {
NumberValue: function () {
return parsimmon
.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/)
.skip(parsimmon.optWhitespace)
.map(Number)
.desc("number");
},
BooleanValue: function() {
BooleanValue: function () {
return parsimmon
.alt(
parsimmon
.regexp(/true/i)
.result(true)
.desc("TRUE"),
parsimmon
.regexp(/false/i)
.result(false)
.desc("FALSE")
parsimmon.regexp(/true/i).result(true).desc("TRUE"),
parsimmon.regexp(/false/i).result(false).desc("FALSE")
)
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
.skip(parsimmon.optWhitespace);
},
NullValue: function() {
NullValue: function () {
return parsimmon
.regexp(/null/i)
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
@@ -211,7 +208,7 @@ const lang = parsimmon.createLanguage({
.result(null)
.desc("NULL");
},
FuncValue: function(r) {
FuncValue: function (r) {
return parsimmon.seqMap(
parsimmon
.regexp(/([a-zA-Z0-9_]+)/, 1)
@@ -226,7 +223,7 @@ const lang = parsimmon.createLanguage({
(f, args) => ["FUNC", f.toUpperCase()].concat(args)
);
},
Value: function(r) {
Value: function (r) {
return parsimmon.alt(
r.NullValue,
r.BooleanValue,
@@ -236,7 +233,7 @@ const lang = parsimmon.createLanguage({
r.FuncValue
);
},
ValueExpression: function(r) {
ValueExpression: function (r) {
return binaryLeft(
parsimmon.string("||").skip(parsimmon.optWhitespace),
binaryLeft(
@@ -259,7 +256,7 @@ const lang = parsimmon.createLanguage({
)
);
},
Comparison: function(r) {
Comparison: function (r) {
return parsimmon.alt(
parsimmon.seqMap(r.ValueExpression, r.IsNullOperator, (p, o) => [o, p]),
parsimmon.seqMap(
@@ -289,8 +286,8 @@ const lang = parsimmon.createLanguage({
)
);
},
Expression: function(r) {
function unary(operatorsParser, nextParser): parsimmon.Parser<{}> {
Expression: function (r) {
function unary(operatorsParser, nextParser): parsimmon.Parser<unknown> {
return parsimmon.seq(operatorsParser, nextParser).or(nextParser);
}
@@ -301,7 +298,7 @@ const lang = parsimmon.createLanguage({
unary(r.NotOperator, r.Comparison.or(r.ValueExpression))
)
).trim(parsimmon.optWhitespace);
}
},
});
export function parse(str: string): Expression {
@@ -309,7 +306,7 @@ export function parse(str: string): Expression {
return lang.Expression.tryParse(str);
}
export function stringify(exp, level = 0): string {
export function stringify(exp: Expression, level = 0): string {
if (!Array.isArray(exp)) return JSON.stringify(exp);
const opLevels = {
@@ -330,7 +327,7 @@ export function stringify(exp, level = 0): string {
"+": 31,
"-": 31,
"*": 32,
"/": 32
"/": 32,
};
const op = exp[0].toUpperCase();
@@ -344,7 +341,7 @@ export function stringify(exp, level = 0): string {
return wrap(
`${exp[1]}(${exp
.slice(2)
.map(e => stringify(e))
.map((e) => stringify(e))
.join(", ")})`
);
} else if (op === "PARAM") {
@@ -354,7 +351,7 @@ export function stringify(exp, level = 0): string {
return wrap(
exp[1]
.slice(1)
.map(p => {
.map((p) => {
if (typeof p === "string") return p;
else return `{${stringify(p)}}`;
})
@@ -393,7 +390,7 @@ export function stringify(exp, level = 0): string {
}
}
export function parseLikePattern(pat, esc): string[] {
export function parseLikePattern(pat: string, esc: string): string[] {
const chars = pat.split("");
for (let i = 0; i < chars.length; ++i) {
@@ -408,5 +405,5 @@ export function parseLikePattern(pat, esc): string[] {
while (chars[i + 1] === "%") chars[++i] = "";
}
}
return chars.filter(c => c);
return chars.filter((c) => c);
}
+36 -26
View File
@@ -24,7 +24,7 @@ import { Expression } from "../types";
const isArray = Array.isArray;
const regExpCache = new WeakMap<object, RegExp>();
const regExpCache = new WeakMap<any, RegExp>();
const REDUCE_SKIP = {};
function reduce(exp, callback): Expression {
@@ -44,7 +44,7 @@ function reduce(exp, callback): Expression {
return exp;
}
export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp {
export function likePatternToRegExp(pat: string, esc = "", flags = ""): RegExp {
const convChars = {
"-": "\\-",
"/": "\\/",
@@ -63,11 +63,11 @@ export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp {
"{": "\\{",
"}": "\\}",
"\\%": ".*",
"\\_": "."
"\\_": ".",
};
let chars = parseLikePattern(pat, esc);
if (!chars.length) return new RegExp("^$", flags);
chars = chars.map(c => convChars[c] || c);
chars = chars.map((c) => convChars[c] || c);
chars[0] = chars[0] === ".*" ? "" : "^" + chars[0];
const l = chars.length - 1;
chars[l] = [".*", ""].includes(chars[l]) ? "" : chars[l] + "$";
@@ -195,14 +195,24 @@ function evalExp(e: Expression): Expression {
}
export function evaluate(
exp,
obj,
exp: Expression,
obj: Record<string, unknown> | ((exp: string) => Expression),
now: number,
cb?: Function
cb?: (exp: Expression) => Expression
): string | number | boolean | null;
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression;
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
return map(exp, e => {
export function evaluate(
exp: Expression,
obj?: Record<string, unknown> | ((exp: string) => Expression),
now?: number,
cb?: (exp: Expression) => Expression
): Expression;
export function evaluate(
exp: Expression,
obj?: Record<string, unknown> | ((exp: string) => Expression),
now?: number,
cb?: (exp: Expression) => Expression
): Expression {
return map(exp, (e) => {
if (cb) e = cb(e);
if (!isArray(e)) return e;
@@ -233,24 +243,24 @@ export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
}
export async function evaluateAsync(
exp,
obj,
exp: Expression,
obj: Record<string, unknown>,
now: number,
cb?: Function
cb?: (exp: Expression) => Promise<Expression>
): Promise<string | number | boolean | null>;
export async function evaluateAsync(
exp,
obj?,
exp: Expression,
obj?: Record<string, unknown>,
now?: number,
cb?: Function
cb?: (exp: Expression) => Promise<Expression>
): Promise<Expression>;
export async function evaluateAsync(
exp,
obj?,
exp: Expression,
obj?: Record<string, unknown>,
now?: number,
cb?: Function
cb?: (exp: Expression) => Promise<Expression>
): Promise<Expression> {
return mapAsync(exp, async e => {
return mapAsync(exp, async (e) => {
if (cb) e = await cb(e);
if (!isArray(e)) return e;
@@ -269,8 +279,8 @@ export async function evaluateAsync(
if (obj && !isArray(e[1])) {
let v = obj[e[1]];
if (v == null) return null;
if (typeof v === "object") v = v.value ? v.value[0] : null;
return v;
if (typeof v === "object") v = v["value"] ? v["value"][0] : null;
return v as Expression;
}
}
return evalExp(e);
@@ -307,21 +317,21 @@ export function or(exp1: Expression, exp2: Expression): Expression {
return res;
}
export function not(exp): Expression {
export function not(exp: Expression): Expression {
if (isArray(exp) && exp[0] === "NOT") return exp[1];
return ["NOT", exp];
}
export function subset(exp1, exp2): boolean {
export function subset(exp1: Expression, exp2: Expression): boolean {
const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]);
if (!isArray(e)) return !e;
const { vars, clauses } = booleanCnf(e);
return !naiveDpll(clauses, vars);
}
export function extractParams(exp): Expression[] {
export function extractParams(exp: Expression): Expression[] {
const params = new Set<Expression>();
map(exp, e => {
map(exp, (e) => {
if (isArray(e) && e[0] === "PARAM") params.add(e[1]);
return e;
});
+1 -1
View File
@@ -39,7 +39,7 @@ function getKey(obj): string {
return k;
}
export default function memoize<T extends Function>(func: T): T {
export default function memoize<T extends (...args: any[]) => any>(func: T): T {
const funcKey = getKey(func);
return ((...args) => {
const key = JSON.stringify(args.map(getKey)) + funcKey;
+7 -7
View File
@@ -73,8 +73,8 @@ export default class PathSet {
public find(
path: Path,
superset: boolean = false,
subset: boolean = false,
superset = false,
subset = false,
depth: number = path.length
): Path[] {
if (path.alias) throw new Error("PathSet does not support aliased paths");
@@ -106,24 +106,24 @@ export default class PathSet {
if (!fragmentIndexSet1) {
if (!fragmentIndexSet2) return [];
if (!res) res = [...fragmentIndexSet2];
else res = res.filter(r => fragmentIndexSet2.has(r));
else res = res.filter((r) => fragmentIndexSet2.has(r));
} else if (!fragmentIndexSet2) {
if (!res) res = [...fragmentIndexSet1];
else res = res.filter(r => fragmentIndexSet1.has(r));
else res = res.filter((r) => fragmentIndexSet1.has(r));
} else {
if (!res) {
res = [...fragmentIndexSet1, ...fragmentIndexSet2];
} else {
res = res.filter(
r => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r)
(r) => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r)
);
}
}
if (!res.length) return res;
}
if (!res) res = [].concat(...lengthIndex.map(a => [...a]));
else res = res.filter(r => lengthIndex.some(a => a.has(r)));
if (!res) res = [].concat(...lengthIndex.map((a) => [...a]));
else res = res.filter((r) => lengthIndex.some((a) => a.has(r)));
return res;
}
+3 -3
View File
@@ -119,7 +119,7 @@ export default class Path {
if (Array.isArray(s)) {
alias |= 1 << i;
const parts = s.map(
al => `${al[0].toString()}:${JSON.stringify(al[1])}`
(al) => `${al[0].toString()}:${JSON.stringify(al[1])}`
);
return `[${parts.join(",")}]`;
} else if (s === "*") {
@@ -160,7 +160,7 @@ export default class Path {
return this._string;
}
public slice(start: number = 0, end: number = this.segments.length): Path {
public slice(start = 0, end: number = this.segments.length): Path {
if (start < 0) start = Math.max(0, this.segments.length + start);
if (end < 0) end = Math.max(0, this.segments.length + end);
@@ -212,7 +212,7 @@ export default class Path {
public stripAlias(): Path {
if (!this.alias) return this;
const segments = this.segments.map(s => (Array.isArray(s) ? "*" : s));
const segments = this.segments.map((s) => (Array.isArray(s) ? "*" : s));
const str = segments.join(".");
let path = cache1.get(str);
+1 -1
View File
@@ -168,7 +168,7 @@ function unitPropagate(clauses, varVals): number[] | 1 | -1 {
return derivedVars;
}
export function naiveDpll(clauses, maxvarnr): boolean {
export function naiveDpll(clauses: unknown[], maxvarnr: number): boolean {
// variable values are 0 if not set, 1 if positive, -1 if negative
const varVals = new Int32Array(maxvarnr + 1);
return satisfiableAt(clauses, varVals, 0, 0);
+12 -36
View File
@@ -38,7 +38,7 @@ const options = {
EXT_DIR: { type: "path", default: resolve(ROOT_DIR, "config/ext") },
MONGODB_CONNECTION_URL: {
type: "string",
default: "mongodb://127.0.0.1/genieacs"
default: "mongodb://127.0.0.1/genieacs",
},
CWMP_WORKER_PROCESSES: { type: "int", default: 0 },
@@ -100,7 +100,7 @@ const options = {
MAX_COMMIT_ITERATIONS: { type: "int", default: 32 },
// Should probably never be changed
DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 }
DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 },
};
const allConfig: { [name: string]: string | number } = {};
@@ -112,37 +112,17 @@ function setConfig(name, value, commandLineArgument = false): boolean {
if (name === "CONFIG_DIR" || name === "config-dir")
configDir = configDir || resolve(ROOT_DIR, value);
if (name === "CWMP_SSL" || name === "cwmp-ssl") {
cwmpSsl =
cwmpSsl ||
String(value)
.toLowerCase()
.trim();
}
if (name === "CWMP_SSL" || name === "cwmp-ssl")
cwmpSsl = cwmpSsl || String(value).toLowerCase().trim();
if (name === "NBI_SSL" || name === "nbi-ssl") {
nbiSsl =
nbiSsl ||
String(value)
.toLowerCase()
.trim();
}
if (name === "NBI_SSL" || name === "nbi-ssl")
nbiSsl = nbiSsl || String(value).toLowerCase().trim();
if (name === "FS_SSL" || name === "fs-ssl") {
fsSsl =
fsSsl ||
String(value)
.toLowerCase()
.trim();
}
if (name === "FS_SSL" || name === "fs-ssl")
fsSsl = fsSsl || String(value).toLowerCase().trim();
if (name === "UI_SSL" || name === "ui-ssl") {
uiSsl =
uiSsl ||
String(value)
.toLowerCase()
.trim();
}
if (name === "UI_SSL" || name === "ui-ssl")
uiSsl = uiSsl || String(value).toLowerCase().trim();
if (name === "FS_HOSTNAME" || name === "fs-hostname")
fsHostname = fsHostname || String(value).trim();
@@ -170,11 +150,7 @@ function setConfig(name, value, commandLineArgument = false): boolean {
case "int":
return Number(val);
case "bool":
return ["true", "1"].includes(
String(val)
.trim()
.toLowerCase()
);
return ["true", "1"].includes(String(val).trim().toLowerCase());
case "string":
return String(val);
case "path":
@@ -308,7 +284,7 @@ export function get(
return null;
}
export function getDefault(optionName): string | number | boolean {
export function getDefault(optionName: string): string | number | boolean {
const option = options[optionName];
if (!option) return null;
+11 -11
View File
@@ -65,10 +65,10 @@ function httpGet(
timeout,
_debug: boolean,
deviceId: string
): Promise<{ statusCode: number; headers: {} }> {
): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> {
return new Promise((resolve, reject) => {
const req = http
.get(options, res => {
.get(options, (res) => {
res.resume();
resolve({ statusCode: res.statusCode, headers: res.headers });
if (_debug) {
@@ -76,12 +76,12 @@ function httpGet(
debug.incomingHttpResponse(res, deviceId, null);
}
})
.on("error", err => {
.on("error", (err) => {
req.abort();
reject(new Error("Device is offline"));
if (_debug) debug.outgoingHttpRequestError(req, deviceId, options, err);
})
.on("socket", socket => {
.on("socket", (socket) => {
socket.setTimeout(timeout);
socket.on("timeout", () => {
req.abort();
@@ -104,10 +104,10 @@ export async function httpConnectionRequest(
options.agent = new http.Agent({
maxSockets: 1,
keepAlive: true
keepAlive: true,
});
let authHeader: {};
let authHeader: Record<string, string>;
let username: string;
let password: string;
@@ -121,8 +121,8 @@ export async function httpConnectionRequest(
opts = Object.assign(
{
headers: {
Authorization: auth.basic(username || "", password || "")
}
Authorization: auth.basic(username || "", password || ""),
},
},
options
);
@@ -137,8 +137,8 @@ export async function httpConnectionRequest(
"GET",
null,
authHeader
)
}
),
},
},
options
);
@@ -172,7 +172,7 @@ export async function httpConnectionRequest(
export async function udpConnectionRequest(
address: string,
authExp: Expression,
sourcePort: number = 0,
sourcePort = 0,
_debug: boolean,
deviceId: string
): Promise<void> {
+66 -65
View File
@@ -43,7 +43,8 @@ import {
Expression,
Task,
SoapMessage,
InformRequest
InformRequest,
AcsResponse,
} from "./types";
import { IncomingMessage, ServerResponse } from "http";
import { Readable } from "stream";
@@ -67,7 +68,7 @@ const stats = {
concurrentRequests: 0,
totalRequests: 0,
droppedRequests: 0,
initiatedSessions: 0
initiatedSessions: 0,
};
async function authenticate(
@@ -195,14 +196,14 @@ async function writeResponse(
if (connection.destroyed) {
logger.accessError({
sessionContext: sessionContext,
message: "Connection dropped"
message: "Connection dropped",
});
} else if (close) {
const isNew = await endSession(sessionContext);
if (isNew) {
logger.accessInfo({
sessionContext: sessionContext,
message: "New device registered"
message: "New device registered",
});
}
} else {
@@ -265,7 +266,7 @@ function recordFault(
message: "Channel has faulted",
fault: fault,
channel: channel,
retries: sessionContext.retries[channel]
retries: sessionContext.retries[channel],
});
}
@@ -288,7 +289,7 @@ function recordFault(
async function inform(
sessionContext: SessionContext,
rpc: SoapMessage
): Promise<{ code: number; headers: {}; data: string }> {
): Promise<{ code: number; headers: Record<string, string>; data: string }> {
const acsResponse = await session.inform(
sessionContext,
rpc.cpeRequest as InformRequest
@@ -297,7 +298,7 @@ async function inform(
const res = soap.response({
id: rpc.id,
acsResponse: acsResponse,
cwmpVersion: sessionContext.cwmpVersion
cwmpVersion: sessionContext.cwmpVersion,
});
const cookiesPath = localCache.getConfig(
@@ -305,7 +306,7 @@ async function inform(
"cwmp.cookiesPath",
{},
sessionContext.timestamp,
e => session.configContextCallback(sessionContext, e)
(e) => session.configContextCallback(sessionContext, e)
);
if (cookiesPath) {
@@ -329,7 +330,7 @@ async function transferComplete(sessionContext, rpc): Promise<void> {
logger.accessWarn({
sessionContext: sessionContext,
message: "Unrecognized command key",
rpc: rpc
rpc: rpc,
});
}
@@ -346,7 +347,7 @@ async function transferComplete(sessionContext, rpc): Promise<void> {
const res = soap.response({
id: rpc.id,
acsResponse: acsResponse,
cwmpVersion: sessionContext.cwmpVersion
cwmpVersion: sessionContext.cwmpVersion,
});
return writeResponse(sessionContext, res);
@@ -397,7 +398,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
"cwmp.retryDelay",
{},
sessionContext.timestamp,
e => session.configContextCallback(sessionContext, e)
(e) => session.configContextCallback(sessionContext, e)
);
if (sessionContext.faults) {
@@ -466,13 +467,13 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
}
}
const declarations = Object.values(parameters).map(v => ({
const declarations = Object.values(parameters).map((v) => ({
path: v,
pathGet: 1,
pathSet: null,
attrGet: { value: 1 },
attrSet: null,
defer: true
defer: true,
}));
const { fault: flt, rpcId: reqId, rpc: acsReq } = await session.rpcRequest(
@@ -496,7 +497,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
const appendProvisionsToFaults = {};
for (const p of filteredPresets) {
if (
evaluate(p.precondition, {}, sessionContext.timestamp, e =>
evaluate(p.precondition, {}, sessionContext.timestamp, (e) =>
session.configContextCallback(sessionContext, e)
)
) {
@@ -529,7 +530,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
const fault = {
code: "preset_loop",
message: "The presets are stuck in an endless configuration loop",
timestamp: sessionContext.timestamp
timestamp: sessionContext.timestamp,
};
recordFault(sessionContext, fault);
// No need to save retryNow
@@ -610,13 +611,13 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
session.clearProvisions(sessionContext);
// Clear expired tasks
sessionContext.tasks = sessionContext.tasks.filter(task => {
sessionContext.tasks = sessionContext.tasks.filter((task) => {
if (!(task.expiry <= sessionContext.timestamp)) return true;
logger.accessInfo({
sessionContext: sessionContext,
message: "Task expired",
task: task
task: task,
});
if (!sessionContext.doneTasks) sessionContext.doneTasks = [];
@@ -633,7 +634,7 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
});
const task = sessionContext.tasks.find(
t => !sessionContext.faults[`task_${t._id}`]
(t) => !sessionContext.faults[`task_${t._id}`]
);
if (!task) return applyPresets(sessionContext);
@@ -646,7 +647,7 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
sessionContext.channels[`task_${task._id}`] = 0;
for (const p of task.parameterNames) {
session.addProvisions(sessionContext, `task_${task._id}`, [
["refresh", p]
["refresh", p],
]);
}
@@ -656,14 +657,14 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
sessionContext.channels[`task_${task._id}`] = 0;
for (const p of task.parameterValues) {
session.addProvisions(sessionContext, `task_${task._id}`, [
["value", p[0], p[1]]
["value", p[0], p[1]],
]);
}
break;
case "refreshObject":
session.addProvisions(sessionContext, `task_${task._id}`, [
["refresh", task.objectName]
["refresh", task.objectName],
]);
break;
case "reboot":
@@ -674,20 +675,20 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
break;
case "download":
session.addProvisions(sessionContext, `task_${task._id}`, [
["download", task.fileType, task.fileName, task.targetFileName || ""]
["download", task.fileType, task.fileName, task.targetFileName || ""],
]);
break;
case "addObject":
alias = (task.parameterValues || [])
.map(p => `${p[0]}:${JSON.stringify(p[1])}`)
.map((p) => `${p[0]}:${JSON.stringify(p[1])}`)
.join(",");
session.addProvisions(sessionContext, `task_${task._id}`, [
["instances", `${task.objectName}.[${alias}]`, "+1"]
["instances", `${task.objectName}.[${alias}]`, "+1"],
]);
break;
case "deleteObject":
session.addProvisions(sessionContext, `task_${task._id}`, [
["instances", task.objectName, 0]
["instances", task.objectName, 0],
]);
break;
default:
@@ -799,13 +800,13 @@ async function sendAcsRequest(
const rpc = {
id: id,
acsRequest: acsRequest,
cwmpVersion: sessionContext.cwmpVersion
cwmpVersion: sessionContext.cwmpVersion,
};
logger.accessInfo({
sessionContext: sessionContext,
message: "ACS request",
rpc: rpc
rpc: rpc,
});
const res = soap.response(rpc);
@@ -821,7 +822,7 @@ async function getSession(connection, sessionId): Promise<SessionContext> {
if (!sessionId) return null;
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 100));
const sessionContextString = await cache.pop(`session_${sessionId}`);
if (!sessionContextString) return null;
@@ -848,7 +849,7 @@ export function onConnection(socket: Socket): void {
const timeoutMsg = logger.flatten({
sessionContext: sessionContext,
message: "Session timeout",
sessionTimestamp: sessionContext.timestamp
sessionTimestamp: sessionContext.timestamp,
});
const timeout =
@@ -883,7 +884,7 @@ setInterval(() => {
droppedRequests: stats.droppedRequests,
totalRequests: stats.totalRequests,
initiatedSessions: stats.initiatedSessions,
pid: process.pid
pid: process.pid,
});
}
@@ -908,20 +909,20 @@ async function getDueTasksAndFaultsAndOperations(
tasks: resParsed.tasks || [],
faults: resParsed.faults || {},
operations: resParsed.operations || {},
ttl: 0
ttl: 0,
};
}
const res2 = await Promise.all([
db.getDueTasks(deviceId, timestamp),
db.getFaults(deviceId),
db.getOperations(deviceId)
db.getOperations(deviceId),
]);
return {
tasks: res2[0][0],
faults: res2[1],
operations: res2[2],
ttl: res2[0][1] || 0
ttl: res2[0][1] || 0,
};
}
@@ -935,7 +936,7 @@ async function cacheDueTasksAndFaultsAndOperations(
const v = {
tasks: null,
faults: null,
operations: null
operations: null,
};
if (tasks.length) v.tasks = tasks;
if (Object.keys(faults).length) v.faults = faults;
@@ -955,7 +956,7 @@ async function cacheDueTasksAndFaultsAndOperations(
async function reportBadState(sessionContext: SessionContext): Promise<void> {
logger.accessError({
message: "Bad session state",
sessionContext: sessionContext
sessionContext: sessionContext,
});
const httpResponse = sessionContext.httpResponse;
currentSessions.delete(httpResponse.connection);
@@ -976,7 +977,7 @@ async function responseUnauthorized(
// Invalid credentials
logger.accessError({
message: "Authentication failure",
sessionContext: sessionContext
sessionContext: sessionContext,
});
resHeaders["Connection"] = "close";
@@ -1007,7 +1008,7 @@ async function responseUnauthorized(
async function processRequest(
sessionContext: SessionContext,
rpc: SoapMessage,
parseWarnings: any[],
parseWarnings: Record<string, unknown>[],
body: string
): Promise<void> {
for (const w of parseWarnings) {
@@ -1027,7 +1028,7 @@ async function processRequest(
"cwmp.debug",
{},
sessionContext.timestamp,
e => session.configContextCallback(sessionContext, e)
(e) => session.configContextCallback(sessionContext, e)
);
if (!sessionContext.timeout) {
@@ -1036,7 +1037,7 @@ async function processRequest(
"cwmp.sessionTimeout",
{},
sessionContext.timestamp,
e => session.configContextCallback(sessionContext, e)
(e) => session.configContextCallback(sessionContext, e)
);
}
@@ -1064,7 +1065,7 @@ async function processRequest(
logger.accessInfo({
sessionContext: sessionContext,
message: "Inform",
rpc: rpc
rpc: rpc,
});
return writeResponse(sessionContext, res);
@@ -1099,7 +1100,7 @@ async function processRequest(
logger.accessInfo({
sessionContext: sessionContext,
message: "CPE request",
rpc: rpc
rpc: rpc,
});
return transferComplete(sessionContext, rpc);
} else if (rpc.cpeRequest.name === "GetRPCMethods") {
@@ -1108,15 +1109,15 @@ async function processRequest(
logger.accessInfo({
sessionContext: sessionContext,
message: "CPE request",
rpc: rpc
rpc: rpc,
});
const res = soap.response({
id: rpc.id,
acsResponse: {
name: "GetRPCMethodsResponse",
methodList: ["Inform", "GetRPCMethods", "TransferComplete"]
},
cwmpVersion: sessionContext.cwmpVersion
methodList: ["Inform", "GetRPCMethods", "TransferComplete"],
} as AcsResponse,
cwmpVersion: sessionContext.cwmpVersion,
});
return writeResponse(sessionContext, res);
} else {
@@ -1136,7 +1137,7 @@ async function processRequest(
logger.accessWarn({
sessionContext: sessionContext,
message: "CPE fault",
rpc: rpc
rpc: rpc,
});
const fault = await session.rpcFault(sessionContext, rpc.id, rpc.cpeFault);
@@ -1179,7 +1180,7 @@ export function listener(
.then(() => {
stats.concurrentRequests -= 1;
})
.catch(err => {
.catch((err) => {
currentSessions.delete(httpResponse.connection);
httpResponse.writeHead(500, { Connection: "close" });
httpResponse.end(`${err.name}: ${err.message}`);
@@ -1208,7 +1209,7 @@ async function listenerAsync(
if (httpRequest.method !== "POST") {
httpResponse.writeHead(405, {
Allow: "POST",
Connection: "close"
Connection: "close",
});
httpResponse.end("405 Method Not Allowed");
return;
@@ -1225,7 +1226,7 @@ async function listenerAsync(
if (!sessionId && stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
httpResponse.writeHead(503, {
"Retry-after": 60,
Connection: "close"
Connection: "close",
});
httpResponse.end("503 Service Unavailable");
stats.droppedRequests += 1;
@@ -1252,7 +1253,7 @@ async function listenerAsync(
const chunks: Buffer[] = [];
let bytes = 0;
stream.on("data", chunk => {
stream.on("data", (chunk) => {
chunks.push(chunk);
bytes += chunk.length;
});
@@ -1290,7 +1291,7 @@ async function listenerAsync(
) {
logger.accessError({
message: "Invalid session",
sessionContext: sessionContext
sessionContext: sessionContext,
});
const _body = "Invalid session";
@@ -1327,7 +1328,7 @@ async function listenerAsync(
if (!charset) {
const parse = parseXmlDeclaration(body);
const e = parse ? parse.find(s => s.localName === "encoding") : null;
const e = parse ? parse.find((s) => s.localName === "encoding") : null;
charset = e ? e.value.toLowerCase() : "utf8";
}
@@ -1340,8 +1341,8 @@ async function listenerAsync(
parseError: msg,
sessionContext: sessionContext || {
httpRequest: httpRequest,
httpResponse: httpResponse
}
httpResponse: httpResponse,
},
});
httpResponse.setHeader("Content-Length", Buffer.byteLength(msg));
httpResponse.writeHead(400, { Connection: "close" });
@@ -1354,10 +1355,10 @@ async function listenerAsync(
cacheSnapshot,
"cwmp.debug",
{
remoteAddress: getRequestOrigin(httpRequest).remoteAddress
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
},
Date.now(),
e => {
(e) => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
return getRequestOrigin(httpRequest).remoteAddress;
return e;
@@ -1383,8 +1384,8 @@ async function listenerAsync(
parseError: error.message.trim(),
sessionContext: sessionContext || {
httpRequest: httpRequest,
httpResponse: httpResponse
}
httpResponse: httpResponse,
},
});
httpResponse.setHeader("Content-Length", Buffer.byteLength(error.message));
httpResponse.writeHead(400, { Connection: "close" });
@@ -1402,10 +1403,10 @@ async function listenerAsync(
cacheSnapshot,
"cwmp.debug",
{
remoteAddress: getRequestOrigin(httpRequest).remoteAddress
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
},
Date.now(),
e => {
(e) => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
return getRequestOrigin(httpRequest).remoteAddress;
return e;
@@ -1425,8 +1426,8 @@ async function listenerAsync(
message: "Invalid session",
sessionContext: sessionContext || {
httpRequest: httpRequest,
httpResponse: httpResponse
}
httpResponse: httpResponse,
},
});
const _body = "Invalid session";
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
@@ -1436,10 +1437,10 @@ async function listenerAsync(
cacheSnapshot,
"cwmp.debug",
{
remoteAddress: getRequestOrigin(httpRequest).remoteAddress
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
},
Date.now(),
e => {
(e) => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
return getRequestOrigin(httpRequest).remoteAddress;
return e;
@@ -1473,7 +1474,7 @@ async function listenerAsync(
tasks: dueTasks,
faults,
operations,
ttl: cacheUntil
ttl: cacheUntil,
} = await getDueTasksAndFaultsAndOperations(
deviceId,
_sessionContext.timestamp
+75 -62
View File
@@ -27,7 +27,7 @@ import {
SessionFault,
Task,
Operation,
Expression
Expression,
} from "./types";
import Path from "./common/path";
@@ -49,7 +49,7 @@ export let client: MongoClient;
export async function connect(): Promise<void> {
clientPromise = MongoClient.connect("" + get("MONGODB_CONNECTION_URL"), {
useNewUrlParser: true
useNewUrlParser: true,
});
client = await clientPromise;
@@ -77,7 +77,7 @@ export async function disconnect(): Promise<void> {
// Optimize projection by removing overlaps
// This can modify the object
function optimizeProjection(obj: {}): {} {
function optimizeProjection(obj: { [path: string]: 1 }): { [path: string]: 1 } {
if (obj[""]) return { "": obj[""] };
const keys = Object.keys(obj).sort();
@@ -104,13 +104,13 @@ export async function fetchDevice(
[
Path.parse("Events"),
timestamp,
{ object: [timestamp, 1], writable: [timestamp, 0] }
{ object: [timestamp, 1], writable: [timestamp, 0] },
],
[
Path.parse("DeviceID"),
timestamp,
{ object: [timestamp, 1], writable: [timestamp, 0] }
]
{ object: [timestamp, 1], writable: [timestamp, 0] },
],
];
const device = await devicesCollection.findOne({ _id: id });
@@ -157,8 +157,8 @@ export async function fetchDevice(
{
object: [+v, 0],
writable: [+v, 0],
value: [+v, [+v, "xsd:dateTime"]]
}
value: [+v, [+v, "xsd:dateTime"]],
},
]);
break;
case "_lastBoot":
@@ -168,8 +168,8 @@ export async function fetchDevice(
{
object: [+v, 0],
writable: [+v, 0],
value: [+v, [+v, "xsd:dateTime"]]
}
value: [+v, [+v, "xsd:dateTime"]],
},
]);
break;
case "_lastBootstrap":
@@ -179,8 +179,8 @@ export async function fetchDevice(
{
object: [+v, 0],
writable: [+v, 0],
value: [+v, [+v, "xsd:dateTime"]]
}
value: [+v, [+v, "xsd:dateTime"]],
},
]);
break;
case "_registered":
@@ -191,8 +191,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [+v, "xsd:dateTime"]]
}
value: [timestamp, [+v, "xsd:dateTime"]],
},
]);
break;
case "_id":
@@ -202,8 +202,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [v as string, "xsd:string"]]
}
value: [timestamp, [v as string, "xsd:string"]],
},
]);
break;
case "_tags":
@@ -211,7 +211,7 @@ export async function fetchDevice(
res.push([
Path.parse("Tags"),
timestamp,
{ object: [timestamp, 1], writable: [timestamp, 0] }
{ object: [timestamp, 1], writable: [timestamp, 0] },
]);
}
@@ -223,8 +223,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 1],
value: [timestamp, [true, "xsd:boolean"]]
}
value: [timestamp, [true, "xsd:boolean"]],
},
]);
}
break;
@@ -236,8 +236,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [v["_Manufacturer"], "xsd:string"]]
}
value: [timestamp, [v["_Manufacturer"], "xsd:string"]],
},
]);
}
@@ -248,8 +248,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [v["_OUI"], "xsd:string"]]
}
value: [timestamp, [v["_OUI"], "xsd:string"]],
},
]);
}
@@ -260,8 +260,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [v["_ProductClass"], "xsd:string"]]
}
value: [timestamp, [v["_ProductClass"], "xsd:string"]],
},
]);
}
@@ -272,8 +272,8 @@ export async function fetchDevice(
{
object: [timestamp, 0],
writable: [timestamp, 0],
value: [timestamp, [v["_SerialNumber"], "xsd:string"]]
}
value: [timestamp, [v["_SerialNumber"], "xsd:string"]],
},
]);
}
}
@@ -398,7 +398,7 @@ export async function saveDevice(
} else {
if (!update["$pull"]["_tags"]) {
update["$pull"]["_tags"] = {
$in: []
$in: [],
};
}
update["$pull"]["_tags"]["$in"].push(path.segments[1]);
@@ -510,7 +510,7 @@ export async function saveDevice(
}
const result = await devicesCollection.updateOne({ _id: deviceId }, update, {
upsert: isNew
upsert: isNew,
});
if (result.result.n !== 1)
@@ -523,7 +523,7 @@ export async function saveDevice(
}
export async function getFaults(
deviceId
deviceId: string
): Promise<{ [channel: string]: SessionFault }> {
const res = await faultsCollection
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
@@ -543,24 +543,31 @@ export async function getFaults(
return faults;
}
export async function saveFault(deviceId, channel, fault): Promise<void> {
export async function saveFault(
deviceId: string,
channel: string,
fault: SessionFault
): Promise<void> {
const id = `${deviceId}:${channel}`;
fault = Object.assign({}, fault);
fault._id = id;
fault.device = deviceId;
fault.channel = channel;
fault.timestamp = new Date(fault.timestamp);
fault.provisions = JSON.stringify(fault.provisions);
await faultsCollection.replaceOne({ _id: id }, fault, { upsert: true });
const f = Object.assign({}, fault) as Record<string, any>;
f["_id"] = id;
f["device"] = deviceId;
f["channel"] = channel;
f["timestamp"] = new Date(fault.timestamp);
f["provisions"] = JSON.stringify(fault.provisions);
await faultsCollection.replaceOne({ _id: id }, f, { upsert: true });
}
export async function deleteFault(deviceId, channel): Promise<void> {
export async function deleteFault(
deviceId: string,
channel: string
): Promise<void> {
await faultsCollection.deleteOne({ _id: `${deviceId}:${channel}` });
}
export async function getDueTasks(
deviceId,
timestamp
deviceId: string,
timestamp: number
): Promise<[Task[], number]> {
const cur = tasksCollection.find({ device: deviceId }).sort(["timestamp"]);
const tasks = [] as Task[];
@@ -593,14 +600,17 @@ export async function getDueTasks(
return [tasks, null];
}
export async function clearTasks(deviceId, taskIds): Promise<void> {
export async function clearTasks(
deviceId: string,
taskIds: string[]
): Promise<void> {
await tasksCollection.deleteMany({
_id: { $in: taskIds.map(id => new ObjectID(id)) }
_id: { $in: taskIds.map((id) => new ObjectID(id)) },
});
}
export async function getOperations(
deviceId
deviceId: string
): Promise<{ [commandKey: string]: Operation }> {
const res = await operationsCollection
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
@@ -620,43 +630,46 @@ export async function getOperations(
}
export async function saveOperation(
deviceId,
commandKey,
operation
deviceId: string,
commandKey: string,
operation: Operation
): Promise<void> {
const id = `${deviceId}:${commandKey}`;
operation = Object.assign({}, operation);
operation._id = id;
operation.timestamp = new Date(operation.timestamp);
operation.provisions = JSON.stringify(operation.provisions);
operation.retries = JSON.stringify(operation.retries);
operation.args = JSON.stringify(operation.args);
const o = Object.assign({}, operation) as Record<string, any>;
o["_id"] = id;
o["timestamp"] = new Date(operation.timestamp);
o["provisions"] = JSON.stringify(operation.provisions);
o["retries"] = JSON.stringify(operation.retries);
o["args"] = JSON.stringify(operation.args);
await operationsCollection.replaceOne({ _id: id }, operation, {
upsert: true
upsert: true,
});
}
export async function deleteOperation(deviceId, commandKey): Promise<void> {
export async function deleteOperation(
deviceId: string,
commandKey: string
): Promise<void> {
await operationsCollection.deleteOne({ _id: `${deviceId}:${commandKey}` });
}
export async function getPresets(): Promise<{}[]> {
export async function getPresets(): Promise<Record<string, any>[]> {
return presetsCollection.find().toArray();
}
export async function getObjects(): Promise<{}[]> {
export async function getObjects(): Promise<Record<string, any>[]> {
return objectsCollection.find().toArray();
}
export async function getProvisions(): Promise<{}[]> {
export async function getProvisions(): Promise<Record<string, any>[]> {
return provisionsCollection.find().toArray();
}
export async function getVirtualParameters(): Promise<{}[]> {
export async function getVirtualParameters(): Promise<Record<string, any>[]> {
return virtualParametersCollection.find().toArray();
}
export function getFiles(): Promise<{}[]> {
export function getFiles(): Promise<Record<string, any>[]> {
return filesCollection.find().toArray();
}
@@ -664,9 +677,9 @@ export async function getConfig(): Promise<
{ id: string; value: Expression }[]
> {
const res = await configCollection.find().toArray();
return res.map(c => ({
return res.map((c) => ({
id: c["_id"],
value: parse(c["value"])
value: parse(c["value"]),
}));
}
+7 -7
View File
@@ -21,7 +21,7 @@ import {
IncomingMessage,
ServerResponse,
ClientRequest,
RequestOptions
RequestOptions,
} from "http";
import { Socket } from "net";
import { appendFileSync } from "fs";
@@ -60,7 +60,7 @@ export function incomingHttpRequest(
method: httpRequest.method,
url: httpRequest.url,
headers: httpRequest.headers,
body: body
body: body,
};
if (DEBUG_FORMAT === "yaml")
@@ -86,7 +86,7 @@ export function outgoingHttpResponse(
connection: getConnectionTimestamp(con),
statusCode: httpResponse.statusCode,
headers: httpResponse.getHeaders(),
body: body
body: body,
};
if (DEBUG_FORMAT === "yaml")
@@ -115,7 +115,7 @@ export function outgoingHttpRequest(
method: options.method || "GET",
url: options.path,
headers: httpRequest.getHeaders(),
body: body
body: body,
};
if (DEBUG_FORMAT === "yaml")
@@ -143,7 +143,7 @@ export function outgoingHttpRequestError(
method: options.method,
url: options.path,
headers: httpRequest.getHeaders(),
error: err.message
error: err.message,
};
if (DEBUG_FORMAT === "yaml")
@@ -169,7 +169,7 @@ export function incomingHttpResponse(
connection: getConnectionTimestamp(httpResponse.connection),
statusCode: httpResponse.statusCode,
headers: httpResponse.headers,
body: body
body: body,
};
if (DEBUG_FORMAT === "yaml")
@@ -193,7 +193,7 @@ export function outgoingUdpMessage(
remoteAddress: remoteAddress,
deviceId: deviceId,
remotePort: remotePort,
body: body
body: body,
};
if (DEBUG_FORMAT === "yaml")
+47 -22
View File
@@ -21,10 +21,15 @@ import Path from "./common/path";
import * as config from "./config";
import * as device from "./device";
import * as scheduling from "./scheduling";
import { SessionContext, Declaration } from "./types";
const MAX_DEPTH = +config.get("MAX_DEPTH");
export function refresh(sessionContext, provision, declarations): boolean {
export function refresh(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (
(provision.length !== 2 || typeof provision[1] !== "string") &&
(provision.length !== 3 ||
@@ -38,7 +43,7 @@ export function refresh(sessionContext, provision, declarations): boolean {
segments.length = MAX_DEPTH;
segments.fill("*", l);
const path = Path.parse(segments.join("."));
const every = 1000 * (provision[2] || 1);
const every = 1000 * ((provision[2] as number) || 1);
const offset = scheduling.variance(sessionContext.deviceId, every);
const t = scheduling.interval(sessionContext.timestamp, every, offset);
@@ -49,14 +54,18 @@ export function refresh(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { object: 1, writable: 1, value: t },
attrSet: null,
defer: true
defer: true,
});
}
return true;
}
export function value(sessionContext, provision, declarations): boolean {
export function value(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (provision.length !== 3 || typeof provision[1] !== "string")
throw new Error("Invalid arguments");
@@ -66,13 +75,17 @@ export function value(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { value: 1 },
attrSet: { value: [provision[2]] },
defer: true
defer: true,
});
return true;
}
export function tag(sessionContext, provision, declarations): boolean {
export function tag(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (
provision.length !== 3 ||
typeof provision[1] !== "string" ||
@@ -86,13 +99,17 @@ export function tag(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { value: 1 },
attrSet: { value: [provision[2]] },
defer: true
defer: true,
});
return true;
}
export function reboot(sessionContext, provision, declarations): boolean {
export function reboot(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (provision.length !== 1) throw new Error("Invalid arguments");
declarations.push({
@@ -101,13 +118,17 @@ export function reboot(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { value: 1 },
attrSet: { value: [sessionContext.timestamp] },
defer: true
defer: true,
});
return true;
}
export function reset(sessionContext, provision, declarations): boolean {
export function reset(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (provision.length !== 1) throw new Error("Invalid arguments");
declarations.push({
@@ -116,13 +137,17 @@ export function reset(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { value: 1 },
attrSet: { value: [sessionContext.timestamp] },
defer: true
defer: true,
});
return true;
}
export function download(sessionContext, provision, declarations): boolean {
export function download(
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[]
): boolean {
if (
(provision.length !== 3 ||
typeof provision[1] !== "string" ||
@@ -137,7 +162,7 @@ export function download(sessionContext, provision, declarations): boolean {
const alias = [
`FileType:${JSON.stringify(provision[1] || "")}`,
`FileName:${JSON.stringify(provision[2] || "")}`,
`TargetFileName:${JSON.stringify(provision[3] || "")}`
`TargetFileName:${JSON.stringify(provision[3] || "")}`,
].join(",");
declarations.push({
@@ -146,7 +171,7 @@ export function download(sessionContext, provision, declarations): boolean {
pathSet: 1,
attrGet: null,
attrSet: null,
defer: true
defer: true,
});
declarations.push({
@@ -155,18 +180,18 @@ export function download(sessionContext, provision, declarations): boolean {
pathSet: null,
attrGet: { value: 1 },
attrSet: { value: [sessionContext.timestamp] },
defer: true
defer: true,
});
return true;
}
export function instances(
sessionContext,
provision,
declarations,
startRevision,
endRevision
sessionContext: SessionContext,
provision: (string | number | boolean)[],
declarations: Declaration[],
startRevision: number,
endRevision: number
): boolean {
if (provision.length !== 3 || typeof provision[1] !== "string")
throw new Error("Invalid arguments");
@@ -184,7 +209,7 @@ export function instances(
pathSet: null,
attrGet: null,
attrSet: null,
defer: true
defer: true,
});
if (endRevision === startRevision) return false;
@@ -203,7 +228,7 @@ export function instances(
pathSet: count,
attrGet: null,
attrSet: null,
defer: true
defer: true,
});
return true;
+24 -15
View File
@@ -18,12 +18,18 @@
*/
import Path from "./common/path";
import { DeviceData, Declaration, Clear } from "./types";
import {
DeviceData,
Declaration,
Clear,
AttributeTimestamps,
Attributes,
} from "./types";
const CHANGE_FLAGS = {
object: 2,
writable: 4,
value: 8
value: 8,
};
function parseBool(v): boolean {
@@ -36,7 +42,7 @@ function parseBool(v): boolean {
export function sanitizeParameterValue(
parameterValue: [string | number | boolean, string]
): [string | number | boolean, string?] {
): [string | number | boolean, string] {
if (parameterValue[0] != null) {
switch (parameterValue[1]) {
case "xsd:boolean":
@@ -96,8 +102,8 @@ export function getAliasDeclarations(
pathSet: null,
attrGet: attrGet,
attrSet: null,
defer: true
}
defer: true,
},
];
if (path.alias) {
@@ -107,7 +113,7 @@ export function getAliasDeclarations(
for (const [p] of alias as [Path, string][]) {
decs = decs.concat(
getAliasDeclarations(parent.concat(p), timestamp, {
value: timestamp
value: timestamp,
})
);
}
@@ -189,10 +195,10 @@ export function unpack(
}
export function clear(
deviceData,
path,
timestamp,
attributes,
deviceData: DeviceData,
path: Path,
timestamp: number,
attributes: AttributeTimestamps,
changeFlags = 0
): void {
const changeTrackers = {};
@@ -271,8 +277,8 @@ function compareEquality(a, b): boolean {
export function set(
deviceData: DeviceData,
path: Path,
timestamp,
attributes,
timestamp: number,
attributes: Attributes,
toClear?: Clear[]
): Clear[] {
path = deviceData.paths.add(path);
@@ -300,7 +306,7 @@ export function set(
attributes.object[1] &&
attributes.object[0] >= (attributes.value ? attributes.value[0] : 0)
)
attributes.value = [attributes.object[0]];
attributes.value = [attributes.object[0], null];
const newAttributes = Object.assign({}, currentAttributes, attributes);
@@ -376,7 +382,7 @@ export function track(
deviceData: DeviceData,
path: Path,
marker: string,
attributes?
attributes?: string[]
): void {
path = deviceData.paths.add(path);
let f = 1;
@@ -393,7 +399,10 @@ export function track(
cur[marker] |= f;
}
export function clearTrackers(deviceData: DeviceData, tracker): void {
export function clearTrackers(
deviceData: DeviceData,
tracker: string | string[]
): void {
if (Array.isArray(tracker)) {
for (const v of deviceData.trackers.values())
for (const t of tracker) delete v[t];
+7 -7
View File
@@ -36,8 +36,8 @@ function messageHandler(message): void {
}
}
export function run(args): Promise<{ fault: Fault; value: any }> {
return new Promise(resolve => {
export function run(args: string[]): Promise<{ fault: Fault; value: any }> {
return new Promise((resolve) => {
const scriptName = args[0];
const id = crypto.randomBytes(8).toString("hex");
@@ -45,16 +45,16 @@ export function run(args): Promise<{ fault: Fault; value: any }> {
if (!processes[scriptName]) {
const p = spawn(ROOT_DIR + "/bin/genieacs-ext", [scriptName], {
stdio: ["inherit", "inherit", "inherit", "ipc"]
stdio: ["inherit", "inherit", "inherit", "ipc"],
});
processes[scriptName] = p;
p.on("error", err => {
p.on("error", (err) => {
if (processes[scriptName] === p) {
if (jobs.delete(id)) {
resolve({
fault: { code: err.name, message: err.message },
value: null
value: null,
});
}
@@ -74,7 +74,7 @@ export function run(args): Promise<{ fault: Fault; value: any }> {
if (jobs.delete(id)) {
resolve({
fault: { code: "timeout", message: "Extension timed out" },
value: null
value: null,
});
}
}, TIMEOUT);
@@ -86,7 +86,7 @@ export function run(args): Promise<{ fault: Fault; value: any }> {
}
function kill(process: ChildProcess): Promise<void> {
return new Promise(resolve => {
return new Promise((resolve) => {
const timeToKill = Date.now() + 5000;
process.kill();
+3 -3
View File
@@ -35,7 +35,7 @@ const FORWARDED_HEADER = "" + config.get("FORWARDED_HEADER");
const cache = new WeakMap<IncomingMessage, RequestOrigin>();
const cidrs: [IPv4 | IPv6, number][] = [];
for (const str of FORWARDED_HEADER.split(",").map(s => s.trim())) {
for (const str of FORWARDED_HEADER.split(",").map((s) => s.trim())) {
try {
cidrs.push(parseCIDR(str));
} catch (err) {
@@ -104,13 +104,13 @@ export function getRequestOrigin(request: IncomingMessage): RequestOrigin {
remoteAddress: soc.remoteAddress,
remotePort: soc.remotePort,
host: request.headers["host"],
encrypted: !!(request.socket as TLSSocket).encrypted
encrypted: !!(request.socket as TLSSocket).encrypted,
};
const header = request.headers["forwarded"];
if (header) {
const ip = parse(soc.remoteAddress) as IPv4;
if (cidrs.some(cidr => ip.match(cidr as [IPv4, number]))) {
if (cidrs.some((cidr) => ip.match(cidr as [IPv4, number]))) {
const parsed = parseForwardedHeader(header);
if (parsed["proto"] === "https") {
+2 -2
View File
@@ -36,7 +36,7 @@ export function listener(
const log = {
message: "Fetch file",
filename: filename,
remoteAddress: getRequestOrigin(request).remoteAddress
remoteAddress: getRequestOrigin(request).remoteAddress,
};
db.filesCollection.findOne({ _id: filename }, (err, file) => {
@@ -52,7 +52,7 @@ export function listener(
response.writeHead(200, {
"Content-Type": file.contentType || "application/octet-stream",
"Content-Length": file.length
"Content-Length": file.length,
});
const bucket = new GridFSBucket(db.client.db());
+1 -1
View File
@@ -31,7 +31,7 @@ const UNDISCOVERED_DEPTH = 7;
// point has been discovered.
export function estimateGpnCount(
gpnPatterns: [Path, number][],
depth: number = 0
depth = 0
): number {
const children: { [segment: string]: [Path, number][] } = {};
const wildcardChildren: [Path, number][] = [];
+50 -49
View File
@@ -95,11 +95,11 @@ export async function getStatus(): Promise<Status> {
filters: !Object.keys(ui["filters"]).length,
device: !Object.keys(ui["device"]).length,
index: !Object.keys(ui["index"]).length,
overview: !Object.keys(ui["overview"]).length
overview: !Object.keys(ui["overview"]).length,
};
}
export async function seed(options): Promise<void> {
export async function seed(options: Record<string, boolean>): Promise<void> {
const resources = {};
const proms = [];
@@ -117,12 +117,12 @@ export async function seed(options): Promise<void> {
role: "admin",
resource: "virtualParameters",
access: 3,
validate: "true"
}
validate: "true",
},
];
resources["users"] = [
{ username: "admin", password: "admin", roles: ["admin"] }
{ username: "admin", password: "admin", roles: ["admin"] },
];
}
@@ -135,7 +135,7 @@ export async function seed(options): Promise<void> {
{ _id: "ui.filters.1.parameter", value: "DeviceID.ProductClass" },
{ _id: "ui.filters.1.type", value: "'string'" },
{ _id: "ui.filters.2.label", value: "'Tag'" },
{ _id: "ui.filters.2.type", value: "'tag'" }
{ _id: "ui.filters.2.type", value: "'tag'" },
]);
}
@@ -149,118 +149,118 @@ export async function seed(options): Promise<void> {
{ _id: "ui.device.2.parameters.0.label", value: "'Last inform'" },
{
_id: "ui.device.2.parameters.0.components.0.type",
value: "'parameter'"
value: "'parameter'",
},
{ _id: "ui.device.2.parameters.0.components.1.chart", value: "'online'" },
{
_id: "ui.device.2.parameters.0.components.1.type",
value: "'overview-dot'"
value: "'overview-dot'",
},
{
_id: "ui.device.2.parameters.0.components.2.type",
value: "'summon-button'"
value: "'summon-button'",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.0",
value: "InternetGatewayDevice.DeviceInfo.HardwareVersion"
value: "InternetGatewayDevice.DeviceInfo.HardwareVersion",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.1",
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion"
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.2",
value:
"InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.MACAddress"
"InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.MACAddress",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.3",
value:
"InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.ExternalIPAddress"
"InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.ExternalIPAddress",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.4",
value: "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.SSID"
value: "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.SSID",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.5",
value:
"InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.KeyPassphrase"
"InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.KeyPassphrase",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.6",
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.HostName"
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.HostName",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.7",
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.IPAddress"
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.IPAddress",
},
{
_id: "ui.device.2.parameters.0.components.2.parameters.8",
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.MACAddress"
value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.MACAddress",
},
{
_id: "ui.device.2.parameters.0.parameter",
value: "DATE_STRING(Events.Inform)"
value: "DATE_STRING(Events.Inform)",
},
{ _id: "ui.device.2.parameters.1.label", value: "'Serial number'" },
{
_id: "ui.device.2.parameters.1.parameter",
value: "DeviceID.SerialNumber"
value: "DeviceID.SerialNumber",
},
{ _id: "ui.device.2.parameters.2.label", value: "'Product class'" },
{
_id: "ui.device.2.parameters.2.parameter",
value: "DeviceID.ProductClass"
value: "DeviceID.ProductClass",
},
{ _id: "ui.device.2.parameters.3.label", value: "'OUI'" },
{
_id: "ui.device.2.parameters.3.parameter",
value: "DeviceID.OUI"
value: "DeviceID.OUI",
},
{ _id: "ui.device.2.parameters.4.label", value: "'Manufacturer'" },
{
_id: "ui.device.2.parameters.4.parameter",
value: "DeviceID.Manufacturer"
value: "DeviceID.Manufacturer",
},
{ _id: "ui.device.2.parameters.5.label", value: "'Hardware version'" },
{
_id: "ui.device.2.parameters.5.parameter",
value: "InternetGatewayDevice.DeviceInfo.HardwareVersion"
value: "InternetGatewayDevice.DeviceInfo.HardwareVersion",
},
{ _id: "ui.device.2.parameters.6.label", value: "'Software version'" },
{
_id: "ui.device.2.parameters.6.parameter",
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion"
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion",
},
{ _id: "ui.device.2.parameters.7.label", value: "'MAC'" },
{
_id: "ui.device.2.parameters.7.parameter",
value:
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress"
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress",
},
{ _id: "ui.device.2.parameters.8.label", value: "'IP'" },
{
_id: "ui.device.2.parameters.8.parameter",
value:
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress"
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress",
},
{ _id: "ui.device.2.parameters.9.label", value: "'WLAN SSID'" },
{
_id: "ui.device.2.parameters.9.parameter",
value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID"
value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID",
},
{ _id: "ui.device.2.parameters.10.label", value: "'WLAN passphrase'" },
{
_id: "ui.device.2.parameters.10.parameter",
value:
"InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase"
"InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase",
},
{ _id: "ui.device.3.type", value: "'parameter-table'" },
{ _id: "ui.device.3.label", value: "'WLAN hosts'" },
{
_id: "ui.device.3.parameter",
value: "InternetGatewayDevice.LANDevice.1.Hosts.Host"
value: "InternetGatewayDevice.LANDevice.1.Hosts.Host",
},
{ _id: "ui.device.3.childParameters.0.label", value: "'Host name'" },
{ _id: "ui.device.3.childParameters.0.parameter", value: "HostName" },
@@ -278,12 +278,12 @@ export async function seed(options): Promise<void> {
{ _id: "ui.device.5.element", value: "'div.container-full-width'" },
{
_id: "ui.device.5.components.0.components.0",
value: "'All parameters'"
value: "'All parameters'",
},
{ _id: "ui.device.5.components.0.element", value: "'h3'" },
{ _id: "ui.device.5.components.0.type", value: "'container'" },
{ _id: "ui.device.5.components.1.type", value: "'all-parameters'" },
{ _id: "ui.device.6.type", value: "'device-actions'" }
{ _id: "ui.device.6.type", value: "'device-actions'" },
]);
}
@@ -298,18 +298,18 @@ export async function seed(options): Promise<void> {
{ _id: "ui.index.2.label", value: "'Software version'" },
{
_id: "ui.index.2.parameter",
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion"
value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion",
},
{ _id: "ui.index.3.label", value: "'IP'" },
{
_id: "ui.index.3.parameter",
value:
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress"
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress",
},
{ _id: "ui.index.4.label", value: "'SSID'" },
{
_id: "ui.index.4.parameter",
value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID"
value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID",
},
{ _id: "ui.index.5.type", value: "'container'" },
{ _id: "ui.index.5.label", value: "'Last inform'" },
@@ -322,7 +322,7 @@ export async function seed(options): Promise<void> {
{ _id: "ui.index.6.label", value: "'Tags'" },
{ _id: "ui.index.6.parameter", value: "Tags" },
{ _id: "ui.index.6.unsortable", value: "true" },
{ _id: "ui.index.6.writable", value: "false" }
{ _id: "ui.index.6.writable", value: "false" },
]);
}
@@ -331,43 +331,44 @@ export async function seed(options): Promise<void> {
{ _id: "ui.overview.charts.online.label", value: "'Online status'" },
{
_id: "ui.overview.charts.online.slices.1_onlineNow.color",
value: "'#31a354'"
value: "'#31a354'",
},
{
_id: "ui.overview.charts.online.slices.1_onlineNow.filter",
value: "Events.Inform > NOW() - 5 * 60 * 1000"
value: "Events.Inform > NOW() - 5 * 60 * 1000",
},
{
_id: "ui.overview.charts.online.slices.1_onlineNow.label",
value: "'Online now'"
value: "'Online now'",
},
{
_id: "ui.overview.charts.online.slices.2_past24.color",
value: "'#a1d99b'"
value: "'#a1d99b'",
},
{
_id: "ui.overview.charts.online.slices.2_past24.filter",
value:
"Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)"
"Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)",
},
{
_id: "ui.overview.charts.online.slices.2_past24.label",
value: "'Past 24 hours'"
value: "'Past 24 hours'",
},
{
_id: "ui.overview.charts.online.slices.3_others.color",
value: "'#e5f5e0'"
value: "'#e5f5e0'",
},
{
_id: "ui.overview.charts.online.slices.3_others.filter",
value: "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)"
value:
"Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)",
},
{
_id: "ui.overview.charts.online.slices.3_others.label",
value: "'Others'"
value: "'Others'",
},
{ _id: "ui.overview.groups.online.label", value: "''" },
{ _id: "ui.overview.groups.online.charts.0", value: "'online'" }
{ _id: "ui.overview.groups.online.charts.0", value: "'online'" },
]);
}
@@ -378,16 +379,16 @@ export async function seed(options): Promise<void> {
weight: 0,
channel: "bootstrap",
events: "0 BOOTSTRAP",
provision: "bootstrap"
provision: "bootstrap",
},
{ _id: "default", weight: 0, channel: "default", provision: "default" },
{ _id: "inform", weight: 0, channel: "inform", provision: "inform" }
{ _id: "inform", weight: 0, channel: "inform", provision: "inform" },
];
resources["provisions"] = [
{ _id: "bootstrap", script: BOOTSTRAP_SCRIPT },
{ _id: "default", script: DEFAULT_SCRIPT },
{ _id: "inform", script: INFORM_SCRIPT }
{ _id: "inform", script: INFORM_SCRIPT },
];
}
+41 -33
View File
@@ -36,7 +36,7 @@ import {
Users,
Permissions,
Config,
UiConfig
UiConfig,
} from "./types";
interface Snapshot {
@@ -107,11 +107,15 @@ function computeHash(snapshot): string {
return h.digest("hex");
}
function flattenObject(src, prefix = "", dst = {}): {} {
function flattenObject(
src: Record<string, unknown>,
prefix = "",
dst = {}
): Record<string, unknown> {
for (const k of Object.keys(src)) {
const v = src[k];
if (typeof v === "object" && !Array.isArray(v))
flattenObject(v, `${prefix}${k}.`, dst);
flattenObject(v as Record<string, unknown>, `${prefix}${k}.`, dst);
else dst[`${prefix}${k}`] = v;
}
return dst;
@@ -121,14 +125,14 @@ async function fetchPresets(): Promise<Preset[]> {
const res = await db.getPresets();
let objects = await db.getObjects();
objects = objects.map(obj => {
objects = objects.map((obj) => {
// Flatten object
obj = flattenObject(obj);
// If no keys are defined, consider all parameters as keys to keep the
// same behavior from v1.0
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;
});
@@ -145,12 +149,9 @@ async function fetchPresets(): Promise<Preset[]> {
if (preset["schedule"]) {
const parts = preset["schedule"].trim().split(/\s+/);
schedule = {
md5: crypto
.createHash("md5")
.update(preset["schedule"])
.digest("hex"),
md5: crypto.createHash("md5").update(preset["schedule"]).digest("hex"),
duration: null,
schedule: null
schedule: null,
};
try {
@@ -160,7 +161,7 @@ async function fetchPresets(): Promise<Preset[]> {
logger.warn({
message: "Invalid preset schedule",
preset: preset["_id"],
schedule: preset["schedule"]
schedule: preset["schedule"],
});
schedule.schedule = false;
}
@@ -208,7 +209,7 @@ async function fetchPresets(): Promise<Preset[]> {
for (const obj of objects) {
if (obj["_id"] === c.object) {
const alias = obj["_keys"]
.map(k => `${k}:${JSON.stringify(obj[k])}`)
.map((k) => `${k}:${JSON.stringify(obj[k])}`)
.join(",");
const p = `${c.name}.[${alias}]`;
_provisions.push(["instances", p, 1]);
@@ -226,7 +227,7 @@ async function fetchPresets(): Promise<Preset[]> {
for (const obj of objects) {
if (obj["_id"] === c.object) {
const alias = obj["_keys"]
.map(k => `${k}:${JSON.stringify(obj[k])}`)
.map((k) => `${k}:${JSON.stringify(obj[k])}`)
.join(",");
const p = `${c.name}.[${alias}]`;
_provisions.push(["instances", p, 0]);
@@ -246,7 +247,7 @@ async function fetchPresets(): Promise<Preset[]> {
schedule: schedule,
events: events,
precondition: precondition,
provisions: _provisions
provisions: _provisions,
});
}
@@ -263,7 +264,9 @@ async function fetchProvisions(): Promise<Provisions> {
.createHash("md5")
.update(r["script"])
.digest("hex");
provisions[r["_id"]].script = new vm.Script(
provisions[
r["_id"]
].script = new vm.Script(
`"use strict";(function(){\n${r["script"]}\n})();`,
{ filename: r["_id"], lineOffset: -1, timeout: 50 }
);
@@ -282,7 +285,9 @@ async function fetchVirtualParameters(): Promise<VirtualParameters> {
.createHash("md5")
.update(r["script"])
.digest("hex");
virtualParameters[r["_id"]].script = new vm.Script(
virtualParameters[
r["_id"]
].script = new vm.Script(
`"use strict";(function(){\n${r["script"]}\n})();`,
{ filename: r["_id"], lineOffset: -1, timeout: 50 }
);
@@ -301,7 +306,7 @@ async function fetchPermissions(): Promise<Permissions> {
permissions[p.role][p.access][p.resource] = {
access: p.access,
filter: parse(p.filter || "true")
filter: parse(p.filter || "true"),
};
if (p.validate)
permissions[p.role][p.access][p.resource].validate = parse(p.validate);
@@ -333,7 +338,7 @@ async function fetchUsers(): Promise<Users> {
users[user._id] = {
password: user.password,
salt: user.salt,
roles: user.roles.split(",").map(s => s.trim())
roles: user.roles.split(",").map((s) => s.trim()),
};
}
@@ -349,7 +354,7 @@ async function fetchConfig(): Promise<[Config, UiConfig]> {
filters: {},
device: {},
index: {},
overview: {}
overview: {},
};
const _config = {};
@@ -376,7 +381,7 @@ async function fetchConfig(): Promise<[Config, UiConfig]> {
async function refresh(): Promise<void> {
if (!nextRefresh) {
await new Promise(resolve => setTimeout(resolve, 20));
await new Promise((resolve) => setTimeout(resolve, 20));
await refresh();
return;
}
@@ -400,7 +405,7 @@ async function refresh(): Promise<void> {
fetchFiles(),
fetchPermissions(),
fetchUsers(),
fetchConfig()
fetchConfig(),
]);
const snapshot = {
@@ -411,7 +416,7 @@ async function refresh(): Promise<void> {
permissions: res[4],
users: res[5],
config: res[6][0],
ui: res[6][1]
ui: res[6][1],
};
if (currentSnapshot) {
@@ -435,29 +440,29 @@ export async function getCurrentSnapshot(): Promise<string> {
return currentSnapshot;
}
export function hasSnapshot(hash): boolean {
export function hasSnapshot(hash: string): boolean {
return snapshots.has(hash);
}
export function getPresets(snapshotKey): Preset[] {
export function getPresets(snapshotKey: string): Preset[] {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.presets;
}
export function getProvisions(snapshotKey): Provisions {
export function getProvisions(snapshotKey: string): Provisions {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.provisions;
}
export function getVirtualParameters(snapshotKey): VirtualParameters {
export function getVirtualParameters(snapshotKey: string): VirtualParameters {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.virtualParameters;
}
export function getFiles(snapshotKey): Files {
export function getFiles(snapshotKey: string): Files {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.files;
@@ -466,7 +471,7 @@ export function getFiles(snapshotKey): Files {
export function getConfig(
snapshotKey: string,
key: string,
context: {},
context: Record<string, unknown>,
now: number,
cb?: (Expression) => Expression
): string | number | boolean | null {
@@ -488,7 +493,7 @@ export function getConfig(
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
"cwmp.maxCommitIterations": "MAX_COMMIT_ITERATIONS",
"cwmp.deviceOnlineThreshold": "DEVICE_ONLINE_THRESHOLD",
"cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT"
"cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT",
};
if (!(key in snapshot.config)) {
@@ -509,28 +514,31 @@ export function getConfig(
return Array.isArray(v) ? null : v;
}
export function getConfigExpression(snapshotKey, key): Expression {
export function getConfigExpression(
snapshotKey: string,
key: string
): Expression {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config[key];
}
export function getUsers(snapshotKey): {} {
export function getUsers(snapshotKey: string): Users {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.users;
}
export function getPermissions(snapshotKey): Permissions {
export function getPermissions(snapshotKey: string): Permissions {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.permissions;
}
export function getUiConfig(snapshotKey): UiConfig {
export function getUiConfig(snapshotKey: string): UiConfig {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
+74 -45
View File
@@ -22,6 +22,14 @@ import * as os from "os";
import * as config from "./config";
import { getRequestOrigin } from "./forwarded";
import {
SessionContext,
AcsRequest,
CpeRequest,
CpeFault,
InformRequest,
Fault,
} from "./types";
const REOPEN_EVERY = 60000;
@@ -67,7 +75,7 @@ function reopen(): void {
if (!(stat && stat.dev === logStat.dev && stat.ino === logStat.ino)) {
logStream.end();
logStream = fs.createWriteStream(null, {
fd: fs.openSync(LOG_FILE, "a")
fd: fs.openSync(LOG_FILE, "a"),
});
logStat = fs.fstatSync(logStream.fd);
}
@@ -91,7 +99,7 @@ function reopen(): void {
) {
accessLogStream.end();
accessLogStream = fs.createWriteStream(null, {
fd: fs.openSync(ACCESS_LOG_FILE, "a")
fd: fs.openSync(ACCESS_LOG_FILE, "a"),
});
accessLogStat = fs.fstatSync(accessLogStream.fd);
}
@@ -105,7 +113,7 @@ function reopen(): void {
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
}
export function init(service, version): void {
export function init(service: string, version: string): void {
defaultMeta.hostname = os.hostname();
defaultMeta.pid = process.pid;
defaultMeta.name = `genieacs-${service}`;
@@ -121,7 +129,7 @@ export function init(service, version): void {
if (ACCESS_LOG_FILE) {
accessLogStream = fs.createWriteStream(null, {
fd: fs.openSync(ACCESS_LOG_FILE, "a")
fd: fs.openSync(ACCESS_LOG_FILE, "a"),
});
accessLogStat = fs.fstatSync(accessLogStream.fd);
}
@@ -147,62 +155,75 @@ export function close(): void {
logStream.end();
}
export function flatten(details): {} {
export function flatten(
details: Record<string, unknown>
): Record<string, unknown> {
if (details.sessionContext) {
details.deviceId = details.sessionContext.deviceId;
const sessionContext = details.sessionContext as SessionContext;
details.deviceId = sessionContext.deviceId;
details.remoteAddress = getRequestOrigin(
details.sessionContext.httpRequest
sessionContext.httpRequest
).remoteAddress;
delete details.sessionContext;
}
if (details.exception) {
details.exceptionName = details.exception.name;
details.exceptionMessage = details.exception.message;
details.exceptionStack = details.exception.stack;
const err = details.exception as Error;
details.exceptionName = err.name;
details.exceptionMessage = err.message;
details.exceptionStack = err.stack;
delete details.exception;
}
if (details.task) {
details.taskId = details.task._id;
details.taskId = details.task["_id"];
delete details.task;
}
if (details.rpc) {
if (details.rpc.acsRequest) {
details.acsRequestId = details.rpc.id;
details.acsRequestName = details.rpc.acsRequest.name;
if (details.rpc.acsRequest.commandKey)
details.acsRequestCommandKey = details.rpc.acsRequest.commandKey;
} else if (details.rpc.cpeRequest) {
details.cpeRequestId = details.rpc.id;
if (details.rpc.cpeRequest.name === "Inform") {
details.informEvent = details.rpc.cpeRequest.event.join(",");
details.informRetryCount = details.rpc.cpeRequest.retryCount;
const rpc = details.rpc as {
id: string;
acsRequest?: AcsRequest;
cpeRequest?: CpeRequest;
cpeFault?: CpeFault;
};
if (rpc.acsRequest) {
details.acsRequestId = rpc.id;
details.acsRequestName = rpc.acsRequest.name;
if (rpc.acsRequest["commandKey"])
details.acsRequestCommandKey = rpc.acsRequest["commandKey"];
} else if (rpc.cpeRequest) {
details.cpeRequestId = rpc.id;
if (rpc.cpeRequest.name === "Inform") {
details.informEvent = (rpc.cpeRequest as InformRequest).event.join(",");
details.informRetryCount = (rpc.cpeRequest as InformRequest).retryCount;
} else {
details.cpeRequestName = details.rpc.cpeRequest.name;
if (details.rpc.cpeRequest.commandKey)
details.cpeRequestCommandKey = details.rpc.cpeRequest.commandKey;
details.cpeRequestName = rpc.cpeRequest.name;
if (rpc.cpeRequest["commandKey"])
details.cpeRequestCommandKey = rpc.cpeRequest["commandKey"];
}
} else if (details.rpc.cpeFault) {
details.acsRequestId = details.rpc.id;
details.cpeFaultCode = details.rpc.cpeFault.detail.faultCode;
details.cpeFaultString = details.rpc.cpeFault.detail.faultString;
} else if (rpc.cpeFault) {
details.acsRequestId = rpc.id;
details.cpeFaultCode = rpc.cpeFault.detail.faultCode;
details.cpeFaultString = rpc.cpeFault.detail.faultString;
}
delete details.rpc;
}
if (details.fault) {
details.faultCode = details.fault.code;
details.faultMessage = details.fault.message;
const fault = details.fault as Fault;
details.faultCode = fault.code;
details.faultMessage = fault.message;
delete details.fault;
}
// For genieacs-ui
if (details.context) {
details.remoteAddress = getRequestOrigin(details.context.req).remoteAddress;
if (details.context.state.user)
details.user = details.context.state.user.username;
details.remoteAddress = getRequestOrigin(
details.context["req"]
).remoteAddress;
if (details.context["state"].user)
details.user = details.context["state"].user.username;
delete details.context;
}
@@ -212,7 +233,10 @@ export function flatten(details): {} {
return details;
}
function formatJson(details, systemd): string {
function formatJson(
details: Record<string, unknown>,
systemd: boolean
): string {
if (systemd) {
let severity = "";
if (details.severity === "info") severity = "<6>";
@@ -225,14 +249,17 @@ function formatJson(details, systemd): string {
return `${JSON.stringify(flatten(details))}${os.EOL}`;
}
function formatSimple(details, systemd): string {
function formatSimple(
details: Record<string, unknown>,
systemd: boolean
): string {
const skip = {
user: true,
remoteAddress: true,
severity: true,
timestamp: true,
message: true,
deviceId: !!details.sessionContext
deviceId: !!details.sessionContext,
};
flatten(details);
@@ -265,12 +292,14 @@ function formatSimple(details, systemd): string {
return `${severity}${remote}${details.message}${meta}${os.EOL}`;
}
return `${details.timestamp} [${details.severity.toUpperCase()}] ${remote}${
return `${
details.timestamp
} [${(details.severity as string).toUpperCase()}] ${remote}${
details.message
}${meta}${os.EOL}`;
}
function log(details): void {
function log(details: Record<string, unknown>): void {
details.timestamp = new Date().toISOString();
if (LOG_FORMAT === "json") {
details = Object.assign({}, defaultMeta, details);
@@ -280,22 +309,22 @@ function log(details): void {
}
}
export function info(details): void {
export function info(details: Record<string, unknown>): void {
details.severity = "info";
log(details);
}
export function warn(details): void {
export function warn(details: Record<string, unknown>): void {
details.severity = "warn";
log(details);
}
export function error(details): void {
export function error(details: Record<string, unknown>): void {
details.severity = "error";
log(details);
}
export function accessLog(details): void {
export function accessLog(details: Record<string, unknown>): void {
details.timestamp = new Date().toISOString();
if (ACCESS_LOG_FORMAT === "json") {
Object.assign(details, defaultMeta);
@@ -305,17 +334,17 @@ export function accessLog(details): void {
}
}
export function accessInfo(details): void {
export function accessInfo(details: Record<string, unknown>): void {
details.severity = "info";
accessLog(details);
}
export function accessWarn(details): void {
export function accessWarn(details: Record<string, unknown>): void {
details.severity = "warn";
accessLog(details);
}
export function accessError(details): void {
export function accessError(details: Record<string, unknown>): void {
details.severity = "error";
accessLog(details);
}
+67 -55
View File
@@ -24,8 +24,8 @@ import { Expression, Fault, Task } from "./types";
const isArray = Array.isArray;
export function processDeviceFilter(filter): Expression {
return map(filter, exp => {
export function processDeviceFilter(filter: Expression): Expression {
return map(filter, (exp) => {
if (!isArray(exp)) return exp;
if (exp[0] === "PARAM") {
@@ -61,10 +61,10 @@ export function processDeviceFilter(filter): Expression {
"LIKE",
"NOT LIKE",
"IS NULL",
"IS NOT NULL"
"IS NOT NULL",
].includes(exp[0])
) {
let e = map(exp, ee => {
let e = map(exp, (ee) => {
if (
isArray(ee) &&
ee[0] === "PARAM" &&
@@ -86,8 +86,8 @@ export function processDeviceFilter(filter): Expression {
});
}
export function processTasksFilter(filter): Expression {
return map(filter, exp => {
export function processTasksFilter(filter: Expression): Expression {
return map(filter, (exp) => {
if (!isArray(exp)) return exp;
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
const e = exp.slice();
@@ -102,8 +102,8 @@ export function processTasksFilter(filter): Expression {
});
}
export function processFaultsFilter(filter): Expression {
return map(filter, exp => {
export function processFaultsFilter(filter: Expression): Expression {
return map(filter, (exp) => {
if (!isArray(exp)) return exp;
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
const e = exp.slice();
@@ -116,7 +116,7 @@ export function processFaultsFilter(filter): Expression {
});
}
export function filterToMongoQuery(exp: Expression): {} {
export function filterToMongoQuery(exp: Expression): Record<string, unknown> {
const ops = {
OR: 0,
AND: 0,
@@ -130,10 +130,10 @@ export function filterToMongoQuery(exp: Expression): {} {
LIKE: 1,
"NOT LIKE": 1,
"IS NULL": 1,
"IS NOT NULL": 1
"IS NOT NULL": 1,
};
function recursive(filter, negate, res = {}): {} {
function recursive(filter, negate, res = {}): Record<string, unknown> {
const op = filter[0];
if (ops[op] === 0) {
@@ -249,7 +249,9 @@ export function filterToMongoQuery(exp: Expression): {} {
return recursive(_exp, false);
}
export function processDeviceProjection(projection: {}): {} {
export function processDeviceProjection(
projection: Record<string, 1>
): Record<string, 1> {
if (!projection) return projection;
const p = {};
for (const [k, v] of Object.entries(projection)) {
@@ -275,7 +277,9 @@ export function processDeviceProjection(projection: {}): {} {
return p;
}
export function processDeviceSort(sort: {}): {} {
export function processDeviceSort(
sort: Record<string, number>
): Record<string, number> {
if (!sort) return sort;
const s = {};
for (const [k, v] of Object.entries(sort)) {
@@ -291,7 +295,7 @@ export function processDeviceSort(sort: {}): {} {
return s;
}
function parseDate(d): number | string {
function parseDate(d: Date): number | string {
const n = +d;
return isNaN(n) ? "" + d : n;
}
@@ -307,45 +311,45 @@ interface FlatDevice {
};
}
export function flattenDevice(device): FlatDevice {
export function flattenDevice(device: Record<string, unknown>): FlatDevice {
function recursive(input, root, output, timestamp): void {
for (const [name, tree] of Object.entries(input)) {
if (!root) {
if (name === "_lastInform") {
output["Events.Inform"] = {
value: [parseDate(tree), "xsd:dateTime"],
value: [parseDate(tree as Date), "xsd:dateTime"],
valueTimestamp: timestamp,
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_registered") {
output["Events.Registered"] = {
value: [parseDate(tree), "xsd:dateTime"],
value: [parseDate(tree as Date), "xsd:dateTime"],
valueTimestamp: timestamp,
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_lastBoot") {
output["Events.1_BOOT"] = {
value: [parseDate(tree), "xsd:dateTime"],
value: [parseDate(tree as Date), "xsd:dateTime"],
valueTimestamp: timestamp,
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_lastBootstrap") {
output["Events.0_BOOTSTRAP"] = {
value: [parseDate(tree), "xsd:dateTime"],
value: [parseDate(tree as Date), "xsd:dateTime"],
valueTimestamp: timestamp,
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_id") {
output["DeviceID.ID"] = {
@@ -354,7 +358,7 @@ export function flattenDevice(device): FlatDevice {
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_deviceId") {
output["DeviceID.Manufacturer"] = {
@@ -363,7 +367,7 @@ export function flattenDevice(device): FlatDevice {
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
output["DeviceID.OUI"] = {
value: [tree["_OUI"], "xsd:string"],
@@ -371,7 +375,7 @@ export function flattenDevice(device): FlatDevice {
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
output["DeviceID.ProductClass"] = {
value: [tree["_ProductClass"], "xsd:string"],
@@ -379,7 +383,7 @@ export function flattenDevice(device): FlatDevice {
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
output["DeviceID.SerialNumber"] = {
value: [tree["_SerialNumber"], "xsd:string"],
@@ -387,14 +391,14 @@ export function flattenDevice(device): FlatDevice {
writable: false,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
} else if (name === "_tags") {
output["Tags"] = {
writable: false,
writableTimestamp: timestamp,
object: true,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
for (const t of tree as string[]) {
@@ -404,7 +408,7 @@ export function flattenDevice(device): FlatDevice {
writable: true,
writableTimestamp: timestamp,
object: false,
objectTimestamp: timestamp
objectTimestamp: timestamp,
};
}
}
@@ -422,7 +426,7 @@ export function flattenDevice(device): FlatDevice {
if (tree["_value"] != null) {
attrs["value"] = [
tree["_value"] instanceof Date ? +tree["_value"] : tree["_value"],
tree["_type"]
tree["_type"],
];
attrs["valueTimestamp"] = +(tree["_timestamp"] || childrenTimestamp);
attrs["object"] = false;
@@ -446,27 +450,27 @@ export function flattenDevice(device): FlatDevice {
}
const newDevice = {};
const timestamp = new Date(device["_lastInform"] || 1).getTime();
const timestamp = new Date((device["_lastInform"] as Date) || 1).getTime();
recursive(device, "", newDevice, timestamp);
return newDevice;
}
export function flattenFault(fault): Fault {
const f = Object.assign({}, fault);
export function flattenFault(fault: unknown): Fault {
const f = Object.assign({}, fault) as Fault;
if (f.timestamp) f.timestamp = +f.timestamp;
if (f.expiry) f.expiry = +f.expiry;
return f;
if (f["expiry"]) f["expiry"] = +f["expiry"];
return f as Fault;
}
export function flattenTask(task): Task {
const t = Object.assign({}, task);
export function flattenTask(task: unknown): Task {
const t = Object.assign({}, task) as Task;
t._id = "" + t._id;
if (t.timestamp) t.timestamp = +t.timestamp;
if (t["timestamp"]) t["timestamp"] = +t["timestamp"];
if (t.expiry) t.expiry = +t.expiry;
return t;
}
export function mongoQueryToFilter(query): Expression {
export function mongoQueryToFilter(query: Record<string, unknown>): Expression {
function recursive(_query): Expression {
const expressions: Expression[] = [];
for (const [k, v] of Object.entries(_query)) {
@@ -554,15 +558,19 @@ export function mongoQueryToFilter(query): Expression {
return recursive(query);
}
export function flattenPreset(preset): {} {
export function flattenPreset(
preset: Record<string, unknown>
): Record<string, unknown> {
const p = Object.assign({}, preset);
if (p.precondition) {
try {
// Try parse to check expression validity
parse(p.precondition);
parse(p.precondition as string);
} catch (error) {
p.precondition = mongoQueryToFilter(JSON.parse(p.precondition));
p.precondition = p.precondition.length ? stringify(p.precondition) : "";
p.precondition = mongoQueryToFilter(JSON.parse(p.precondition as string));
p.precondition = (p.precondition as string).length
? stringify(p.precondition as Expression)
: "";
}
}
@@ -574,7 +582,7 @@ export function flattenPreset(preset): {} {
const provision = p.configurations[0];
if (
p.configurations.length === 1 &&
(p.configurations as any[]).length === 1 &&
provision.type === "provision" &&
provision.name &&
provision.name.length
@@ -589,30 +597,34 @@ export function flattenPreset(preset): {} {
return p;
}
export function flattenFile(file): {} {
export function flattenFile(
file: Record<string, unknown>
): Record<string, unknown> {
const f = {};
f["_id"] = file["_id"];
if (file.metadata) {
f["metadata.fileType"] = file.metadata.fileType || "";
f["metadata.oui"] = file.metadata.oui || "";
f["metadata.productClass"] = file.metadata.productClass || "";
f["metadata.version"] = file.metadata.version || "";
f["metadata.fileType"] = file["metadata"]["fileType"] || "";
f["metadata.oui"] = file["metadata"]["oui"] || "";
f["metadata.productClass"] = file["metadata"]["productClass"] || "";
f["metadata.version"] = file["metadata"]["version"] || "";
}
return f;
}
export function preProcessPreset(data): {} {
export function preProcessPreset(
data: Record<string, unknown>
): Record<string, unknown> {
const preset = Object.assign({}, data);
if (!preset.precondition) preset.precondition = "";
// Try parse to check expression validity
parse(preset.precondition);
parse(preset.precondition as string);
preset.weight = parseInt(preset.weight) || 0;
preset.weight = parseInt(preset.weight as string) || 0;
const events = {};
if (preset.events) {
for (let e of preset.events.split(",")) {
for (let e of (preset.events as string).split(",")) {
let v = true;
e = e.trim();
if (e.startsWith("-")) {
@@ -630,7 +642,7 @@ export function preProcessPreset(data): {} {
const configuration = {
type: "provision",
name: preset.provision,
args: null
args: null,
};
if (preset.provisionArgs)
+64 -57
View File
@@ -25,6 +25,7 @@ import * as config from "./config";
import * as db from "./db";
import * as query from "./query";
import * as apiFunctions from "./api-functions";
import { IncomingMessage, ServerResponse } from "http";
import * as cache from "./cache";
import { version as VERSION } from "../package.json";
import { ping } from "./ping";
@@ -42,7 +43,7 @@ const PROVISIONS_REGEX = /^\/provisions\/([a-zA-Z0-9\-_%]+)\/?$/;
const VIRTUAL_PARAMETERS_REGEX = /^\/virtual_parameters\/([a-zA-Z0-9\-_%]+)\/?$/;
const FAULTS_REGEX = /^\/faults\/([a-zA-Z0-9\-_%:]+)\/?$/;
function throwError(err, httpResponse?): never {
function throwError(err: Error, httpResponse?: ServerResponse): never {
if (httpResponse) {
httpResponse.writeHead(500, { Connection: "close" });
httpResponse.end(`${err.name}: ${err.message}`);
@@ -50,7 +51,10 @@ function throwError(err, httpResponse?): never {
throw err;
}
export function listener(request, response): void {
export function listener(
request: IncomingMessage,
response: ServerResponse
): void {
const chunks = [];
let bytes = 0;
response.setHeader("GenieACS-Version", VERSION);
@@ -85,7 +89,7 @@ export function listener(request, response): void {
{ _id: presetName },
preset,
{ upsert: true },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -94,7 +98,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -102,7 +106,7 @@ export function listener(request, response): void {
}
);
} else if (request.method === "DELETE") {
db.presetsCollection.deleteOne({ _id: presetName }, err => {
db.presetsCollection.deleteOne({ _id: presetName }, (err) => {
if (err) return void throwError(err, response);
cache
@@ -111,7 +115,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -132,7 +136,7 @@ export function listener(request, response): void {
{ _id: objectName },
object,
{ upsert: true },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -141,7 +145,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -149,7 +153,7 @@ export function listener(request, response): void {
}
);
} else if (request.method === "DELETE") {
db.objectsCollection.deleteOne({ _id: objectName }, err => {
db.objectsCollection.deleteOne({ _id: objectName }, (err) => {
if (err) return void throwError(err, response);
cache
@@ -158,7 +162,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -175,7 +179,7 @@ export function listener(request, response): void {
if (request.method === "PUT") {
const object = {
_id: provisionName,
script: body.toString()
script: body.toString(),
};
try {
@@ -190,7 +194,7 @@ export function listener(request, response): void {
{ _id: provisionName },
object,
{ upsert: true },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -199,7 +203,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -207,7 +211,7 @@ export function listener(request, response): void {
}
);
} else if (request.method === "DELETE") {
db.provisionsCollection.deleteOne({ _id: provisionName }, err => {
db.provisionsCollection.deleteOne({ _id: provisionName }, (err) => {
if (err) return void throwError(err, response);
cache
@@ -216,7 +220,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -233,7 +237,7 @@ export function listener(request, response): void {
if (request.method === "PUT") {
const object = {
_id: virtualParameterName,
script: body.toString()
script: body.toString(),
};
try {
@@ -248,7 +252,7 @@ export function listener(request, response): void {
{ _id: virtualParameterName },
object,
{ upsert: true },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -257,7 +261,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -267,7 +271,7 @@ export function listener(request, response): void {
} else if (request.method === "DELETE") {
db.virtualParametersCollection.deleteOne(
{ _id: virtualParameterName },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -276,7 +280,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -295,7 +299,7 @@ export function listener(request, response): void {
db.devicesCollection.updateOne(
{ _id: deviceId },
{ $addToSet: { _tags: tag } },
err => {
(err) => {
if (err) return void throwError(err, response);
response.writeHead(200);
response.end();
@@ -305,7 +309,7 @@ export function listener(request, response): void {
db.devicesCollection.updateOne(
{ _id: deviceId },
{ $pull: { _tags: tag } },
err => {
(err) => {
if (err) return void throwError(err, response);
response.writeHead(200);
@@ -323,12 +327,12 @@ export function listener(request, response): void {
);
const deviceId = faultId.split(":", 1)[0];
const channel = faultId.slice(deviceId.length + 1);
db.faultsCollection.deleteOne({ _id: faultId }, err => {
db.faultsCollection.deleteOne({ _id: faultId }, (err) => {
if (err) return void throwError(err, response);
if (channel.startsWith("task_")) {
const objId = new mongodb.ObjectID(channel.slice(5));
return void db.tasksCollection.deleteOne({ _id: objId }, err => {
return void db.tasksCollection.deleteOne({ _id: objId }, (err) => {
if (err) return void throwError(err, response);
cache
@@ -337,7 +341,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -351,7 +355,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -382,17 +386,20 @@ export function listener(request, response): void {
const taskTimeout =
(urlParts.query.timeout &&
parseInt(urlParts.query.timeout as string)) ||
config.get("DEVICE_ONLINE_THRESHOLD", deviceId);
(config.get(
"DEVICE_ONLINE_THRESHOLD",
deviceId
) as number);
apiFunctions
.watchTask(deviceId, task._id, taskTimeout)
.then(status => {
.then((status) => {
if (status === "timeout") {
response.writeHead(
202,
"Task queued but not processed",
{
"Content-Type": "application/json"
"Content-Type": "application/json",
}
);
response.end(JSON.stringify(task));
@@ -404,44 +411,44 @@ export function listener(request, response): void {
return void throwError(err, response);
response.writeHead(202, "Task faulted", {
"Content-Type": "application/json"
"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));
}
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
});
})
.catch(err => {
.catch((err) => {
response.writeHead(202, err.message, {
"Content-Type": "application/json"
"Content-Type": "application/json",
});
response.end(JSON.stringify(task));
});
} else {
response.writeHead(202, {
"Content-Type": "application/json"
"Content-Type": "application/json",
});
response.end(JSON.stringify(task));
}
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
});
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -454,7 +461,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
response.writeHead(504);
response.end(`${err.name}: ${err.message}`);
});
@@ -487,12 +494,12 @@ export function listener(request, response): void {
const deviceId = task.device;
db.tasksCollection.deleteOne(
{ _id: new mongodb.ObjectID(taskId) },
err => {
(err) => {
if (err) return void throwError(err, response);
db.faultsCollection.deleteOne(
{ _id: `${deviceId}:task_${taskId}` },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -501,7 +508,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -527,7 +534,7 @@ export function listener(request, response): void {
const deviceId = task.device;
db.faultsCollection.deleteOne(
{ _id: `${deviceId}:task_${taskId}` },
err => {
(err) => {
if (err) return void throwError(err, response);
cache
@@ -536,7 +543,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -562,7 +569,7 @@ export function listener(request, response): void {
fileType: request.headers.filetype,
oui: request.headers.oui,
productClass: request.headers.productclass,
version: request.headers.version
version: request.headers.version,
};
const bucket = new mongodb.GridFSBucket(db.client.db());
bucket.delete((filename as unknown) as mongodb.ObjectId, () => {
@@ -570,11 +577,11 @@ export function listener(request, response): void {
filename,
filename,
{
metadata: metadata
metadata: metadata,
}
);
uploadStream.on("error", err => {
uploadStream.on("error", (err) => {
throwError(err, response);
});
@@ -585,7 +592,7 @@ export function listener(request, response): void {
});
} else if (request.method === "DELETE") {
const bucket = new mongodb.GridFSBucket(db.client.db());
bucket.delete((filename as unknown) as mongodb.ObjectId, err => {
bucket.delete((filename as unknown) as mongodb.ObjectId, (err) => {
if (err) {
if (err.message.startsWith("FileNotFound")) {
response.writeHead(404);
@@ -618,7 +625,7 @@ export function listener(request, response): void {
response.writeHead(200, {
"Content-Type": "text/plain",
"Cache-Control": "no-cache"
"Cache-Control": "no-cache",
});
response.end(stdout);
});
@@ -638,7 +645,7 @@ export function listener(request, response): void {
response.writeHead(200);
response.end();
})
.catch(err => {
.catch((err) => {
setTimeout(() => {
throwError(err, response);
});
@@ -686,15 +693,15 @@ export function listener(request, response): void {
break;
case "tasks":
q = query.sanitizeQueryTypes(q, {
_id: v => new mongodb.ObjectID(v),
timestamp: v => new Date(v),
retries: Number
_id: (v) => new mongodb.ObjectID(v as string),
timestamp: (v) => new Date(v as number),
retries: Number,
});
break;
case "faults":
q = query.sanitizeQueryTypes(q, {
timestamp: v => new Date(v),
retries: Number
timestamp: (v) => new Date(v as number),
retries: Number,
});
}
@@ -730,7 +737,7 @@ export function listener(request, response): void {
response.writeHead(200, {
"Content-Type": "application/json",
total: total
total: total,
});
if (request.method === "HEAD") {
@@ -741,11 +748,11 @@ export function listener(request, response): void {
response.write("[\n");
i = 0;
cur.forEach(
item => {
(item) => {
if (i++) response.write(",\n");
response.write(JSON.stringify(item));
},
err => {
(err) => {
if (err) return void throwError(err);
response.end("\n]");
}
+5 -5
View File
@@ -20,7 +20,7 @@
import { platform } from "os";
import { exec } from "child_process";
interface Ping {
export interface PingResult {
packetsTransmitted: number;
packetsReceived: number;
packetLoss: number;
@@ -32,7 +32,7 @@ interface Ping {
export function ping(
host: string,
callback: (err, res?, stdout?) => void
callback: (err: Error, res?: PingResult, stdout?: string) => void
): void {
let cmd: string, parseRegExp1: RegExp, parseRegExp2: RegExp;
switch (platform()) {
@@ -55,7 +55,7 @@ export function ping(
}
exec(cmd, (err, stdout) => {
let parsed: Ping;
let parsed: PingResult;
if (stdout) {
const m1 = stdout.match(parseRegExp1);
if (m1) {
@@ -66,7 +66,7 @@ export function ping(
min: +m1[4],
avg: +m1[5],
max: +m1[6],
mdev: +m1[7]
mdev: +m1[7],
};
} else {
const m2 = stdout.match(parseRegExp2);
@@ -78,7 +78,7 @@ export function ping(
min: null,
avg: null,
max: null,
mdev: null
mdev: null,
};
}
}
+18 -98
View File
@@ -17,7 +17,9 @@
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
*/
import * as common from "./common";
function isObject(obj: any): boolean {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function stringToRegexp(input, flags?): RegExp | false {
if (input.indexOf("*") === -1) return false;
@@ -34,8 +36,8 @@ function stringToRegexp(input, flags?): RegExp | false {
}
function normalize(input): any {
if (common.typeOf(input) === common.STRING_TYPE) {
const vals = [input];
if (typeof input === "string") {
const vals: any = [input];
const m = /^\/(.*?)\/(g?i?m?y?)$/.exec(input);
if (m) vals.push({ $regex: new RegExp(m[1], m[2]) });
@@ -52,14 +54,14 @@ function normalize(input): any {
return input;
}
export function expandValue(value): any[] {
if (common.typeOf(value) === common.ARRAY_TYPE) {
function expandValue(value: unknown): unknown[] {
if (Array.isArray(value)) {
let a = [];
for (const j of value) a = a.concat(expandValue(j));
return [a];
} else if (common.typeOf(value) !== common.OBJECT_TYPE) {
} else if (!isObject(value)) {
const n = normalize(value);
if (common.typeOf(n) !== common.ARRAY_TYPE) return [n];
if (!Array.isArray(n)) return [n];
else return n;
}
@@ -103,12 +105,14 @@ function permute(param, val): any[] {
return conditions;
}
export function expand(query): {} {
export function expand(
query: Record<string, unknown>
): Record<string, unknown> {
const newQuery = {};
for (const [k, v] of Object.entries(query)) {
if (k[0] === "$") {
// Operator
newQuery[k] = (v as any[]).map(e => expand(e));
newQuery[k] = (v as any[]).map((e) => expand(e));
} else {
const conditions = permute(k, v);
if (conditions.length > 1) {
@@ -129,100 +133,16 @@ export function expand(query): {} {
return newQuery;
}
function testExpressions(params, expressions, lop): boolean {
for (const f of expressions) {
const res = test(params, f);
switch (lop) {
case "$and":
if (!res) return false;
break;
case "$or":
if (res) return true;
break;
case "$nor":
if (res) return false;
}
}
switch (lop) {
case "$and":
return true;
case "$or":
return false;
case "$nor":
return true;
default:
throw new Error("Unknown logical operator");
}
}
export function test(params, query): boolean {
let res;
for (const [k, v] of Object.entries(query)) {
if (k.charAt(0) === "$") {
// Logical operator
res = testExpressions(params, v, k);
} else {
const value = params[k];
if (common.typeOf(v) !== common.OBJECT_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;
} else {
if (common.typeOf(v) === common.REGEXP_TYPE)
res = (v as RegExp).test(value);
else res = v === value;
}
} else {
for (const [k2, v2] of Object.entries(v)) {
switch (k2) {
case "$ne":
if (common.typeOf(value) === common.ARRAY_TYPE)
res = value.indexOf(v2) === -1;
else res = value !== v2;
break;
case "$lt":
res = value < v2;
break;
case "$lte":
res = value <= v2;
break;
case "$gt":
res = value > v2;
break;
case "$gte":
res = value >= v2;
break;
case "$regex":
res = v2.test(value);
break;
case "$in":
throw new Error("Operator not supported");
case "$nin":
throw new Error("Operator not supported");
case "$all":
throw new Error("Operator not supported");
case "$exists":
throw new Error("Operator not supported");
default:
throw new Error("Operator not supported");
}
}
}
}
if (!res) return false;
}
return true;
}
export function sanitizeQueryTypes(query, types): {} {
export function sanitizeQueryTypes(
query: Record<string, unknown>,
types: Record<string, (v: unknown) => unknown>
): Record<string, unknown> {
for (const [k, v] of Object.entries(query)) {
if (k[0] === "$") {
// Logical operator
for (const vv of v as any[]) sanitizeQueryTypes(vv, types);
} else if (k in types) {
if (common.typeOf(v) === common.OBJECT_TYPE) {
if (isObject(v)) {
for (const [kk, vv] of Object.entries(v)) {
switch (kk) {
case "$in":
+20 -20
View File
@@ -127,7 +127,7 @@ function random(): number {
return state.rng();
}
random.seed = function(s) {
random.seed = function (s) {
state.rng = seedrandom(s);
};
@@ -135,7 +135,7 @@ class ParameterWrapper {
public constructor(path: Path, attributes, unpacked?, unpackedRevision?) {
for (const attrName of attributes) {
Object.defineProperty(this, attrName, {
get: function() {
get: function () {
if (state.uncommitted) commit();
if (state.revision !== unpackedRevision) {
@@ -157,12 +157,12 @@ class ParameterWrapper {
if (!attr) return UNDEFINED;
return attr[1];
}
},
});
}
Object.defineProperty(this, "path", {
get: function() {
get: function () {
if (state.uncommitted) commit();
if (state.revision !== unpackedRevision) {
@@ -177,11 +177,11 @@ class ParameterWrapper {
if (!unpacked.length) return UNDEFINED;
return unpacked[0].toString();
}
},
});
Object.defineProperty(this, "size", {
get: function() {
get: function () {
if (state.uncommitted) commit();
if (state.revision !== unpackedRevision) {
@@ -196,10 +196,10 @@ class ParameterWrapper {
if (!unpacked.length) return UNDEFINED;
return unpacked.length;
}
},
});
this[Symbol.iterator] = function*() {
this[Symbol.iterator] = function* () {
if (state.uncommitted) commit();
if (state.revision !== unpackedRevision) {
@@ -235,7 +235,7 @@ function declare(
pathSet: null,
attrGet: null,
attrSet: null,
defer: true
defer: true,
};
const attrs = new Set();
@@ -290,9 +290,9 @@ function commit(): void {
}
}
function ext(): any {
function ext(...args: unknown[]): any {
++state.extCounter;
const extCall = Array.from(arguments).map(String);
const extCall = args.map(String);
const key = `${state.revision}: ${JSON.stringify(extCall)}`;
if (key in state.sessionContext.extensionsCache)
@@ -302,11 +302,11 @@ function ext(): any {
throw EXT;
}
function log(msg: string, meta: {}): void {
function log(msg: string, meta: Record<string, unknown>): void {
if (state.revision === state.maxRevision && state.extCounter >= 0) {
const details = Object.assign({}, meta, {
sessionContext: state.sessionContext,
message: `Script: ${msg}`
message: `Script: ${msg}`,
});
delete details["hostname"];
@@ -342,8 +342,8 @@ function errorToFault(err: Error): Fault {
message: err.message,
detail: {
name: err.name,
message: err.message
}
message: err.message,
},
};
if (err.stack) {
@@ -365,7 +365,7 @@ function errorToFault(err: Error): Fault {
export async function run(
script: vm.Script,
globals: {},
globals: Record<string, unknown>,
sessionContext: SessionContext,
startRevision: number,
maxRevision: number,
@@ -380,7 +380,7 @@ export async function run(
extensions: {},
clear: [],
rng: null,
extCounter: extCounter
extCounter: extCounter,
};
for (const n of Object.keys(context)) delete context[n];
@@ -403,7 +403,7 @@ export async function run(
clear: null,
declare: null,
done: false,
returnValue: null
returnValue: null,
};
}
}
@@ -423,7 +423,7 @@ export async function run(
clear: null,
declare: null,
done: false,
returnValue: null
returnValue: null,
};
}
@@ -443,6 +443,6 @@ export async function run(
clear: _state.clear,
declare: _state.declarations,
done: status === 0,
returnValue: ret
returnValue: ret,
};
}
+15 -10
View File
@@ -21,10 +21,7 @@ import * as crypto from "crypto";
import * as later from "later";
function md532(str): number {
const digest = crypto
.createHash("md5")
.update(str)
.digest();
const digest = crypto.createHash("md5").update(str).digest();
return (
digest.readUInt32LE(0) ^
digest.readUInt32LE(4) ^
@@ -33,29 +30,37 @@ function md532(str): number {
);
}
export function variance(deviceId, vrnc): number {
export function variance(deviceId: string, vrnc: number): number {
return (md532(deviceId) >>> 0) % vrnc;
}
export function interval(timestamp, intrvl, offset = 0): number {
export function interval(
timestamp: number,
intrvl: number,
offset = 0
): number {
return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset;
}
export function parseCron(cronExp): any {
export function parseCron(cronExp: string): any {
const parts = cronExp.trim().split(/\s+/);
if (parts.length === 5) parts.unshift("*");
return later.schedule(later.parse.cron(parts.join(" "), true));
}
export function cron(timestamp, schedule, offset = 0): number[] {
export function cron(
timestamp: number,
schedule: unknown,
offset = 0
): number[] {
// TODO later.js doesn't throw erorr if expression is invalid!
const ret = [0, 0];
const prev = schedule.prev(1, new Date(timestamp + offset));
const prev = (schedule as any).prev(1, new Date(timestamp + offset));
if (prev) ret[0] = prev.setMilliseconds(0) - offset;
const next = schedule.next(1, new Date(timestamp + offset + 1000));
const next = (schedule as any).next(1, new Date(timestamp + offset + 1000));
if (next) ret[1] = next.setMilliseconds(0) - offset;
return ret;
+9 -8
View File
@@ -20,6 +20,7 @@
import * as fs from "fs";
import * as http from "http";
import * as https from "https";
import { Socket } from "net";
import * as path from "path";
import { ROOT_DIR } from "./config";
@@ -52,12 +53,12 @@ function closeServer(timeout, callback): void {
}
export function start(
port,
networkInterface,
ssl,
_listener,
onConnection?,
keepAliveTimeout: number = -1
port: number,
networkInterface: string,
ssl: { key: string; cert: string },
_listener: http.RequestListener,
onConnection?: (socket: Socket) => void,
keepAliveTimeout = -1
): void {
listener = _listener;
@@ -65,10 +66,10 @@ export function start(
const options = {
key: ssl.key
.split(":")
.map(f => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))),
.map((f) => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))),
cert: ssl.cert
.split(":")
.map(f => fs.readFileSync(path.resolve(ROOT_DIR, f.trim())))
.map((f) => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))),
};
server = https.createServer(options, listener);
+351 -342
View File
File diff suppressed because it is too large. Load diff
+80 -63
View File
@@ -22,7 +22,7 @@ import {
Element,
parseAttrs,
encodeEntities,
decodeEntities
decodeEntities,
} from "./xml-parser";
import memoize from "./common/memoize";
import { version as VERSION } from "../package.json";
@@ -36,7 +36,8 @@ import {
SpvFault,
CpeFault,
SoapMessage,
TransferCompleteRequest
TransferCompleteRequest,
AcsRequest,
} from "./types";
const SERVER_NAME = `GenieACS/${VERSION}`;
@@ -47,39 +48,39 @@ const NAMESPACES = {
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
xsd: "http://www.w3.org/2001/XMLSchema",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cwmp: "urn:dslforum-org:cwmp-1-0"
cwmp: "urn:dslforum-org:cwmp-1-0",
},
"1.1": {
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
xsd: "http://www.w3.org/2001/XMLSchema",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cwmp: "urn:dslforum-org:cwmp-1-1"
cwmp: "urn:dslforum-org:cwmp-1-1",
},
"1.2": {
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
xsd: "http://www.w3.org/2001/XMLSchema",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cwmp: "urn:dslforum-org:cwmp-1-2"
cwmp: "urn:dslforum-org:cwmp-1-2",
},
"1.3": {
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
xsd: "http://www.w3.org/2001/XMLSchema",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cwmp: "urn:dslforum-org:cwmp-1-2"
cwmp: "urn:dslforum-org:cwmp-1-2",
},
"1.4": {
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
xsd: "http://www.w3.org/2001/XMLSchema",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cwmp: "urn:dslforum-org:cwmp-1-3"
}
cwmp: "urn:dslforum-org:cwmp-1-3",
},
};
let warnings;
let warnings: Record<string, unknown>[];
const memoizedParseAttrs = memoize(parseAttrs);
@@ -93,14 +94,16 @@ function parseBool(v): boolean {
function event(xml: Element): string[] {
return xml.children
.filter(n => n.localName === "EventStruct")
.map(c => c.children.find(n => n.localName === "EventCode").text.trim());
.filter((n) => n.localName === "EventStruct")
.map((c) =>
c.children.find((n) => n.localName === "EventCode").text.trim()
);
}
function parameterInfoList(xml: Element): [string, boolean][] {
return xml.children
.filter(e => e.localName === "ParameterInfoStruct")
.map<[string, boolean]>(e => {
.filter((e) => e.localName === "ParameterInfoStruct")
.map<[string, boolean]>((e) => {
let param: string, value: string;
for (const c of e.children) {
switch (c.localName) {
@@ -118,7 +121,7 @@ function parameterInfoList(xml: Element): [string, boolean][] {
if (parsed === null) {
warnings.push({
message: "Invalid writable attribute",
parameter: param
parameter: param,
});
parsed = false;
}
@@ -129,7 +132,7 @@ function parameterInfoList(xml: Element): [string, boolean][] {
const getValueType = memoize((str: string) =>
parseAttrs(str)
.find(s => s.localName === "type")
.find((s) => s.localName === "type")
.value.trim()
);
@@ -137,8 +140,8 @@ function parameterValueList(
xml: Element
): [string, string | number | boolean, string][] {
return xml.children
.filter(e => e.localName === "ParameterValueStruct")
.map<[string, string | number | boolean, string]>(e => {
.filter((e) => e.localName === "ParameterValueStruct")
.map<[string, string | number | boolean, string]>((e) => {
let valueElement: Element, param: string;
for (const c of e.children) {
switch (c.localName) {
@@ -160,7 +163,7 @@ function parameterValueList(
if (parsed === null) {
warnings.push({
message: "Invalid value attribute",
parameter: param
parameter: param,
});
parsed = value;
}
@@ -169,7 +172,7 @@ function parameterValueList(
if (isNaN(parsed)) {
warnings.push({
message: "Invalid value attribute",
parameter: param
parameter: param,
});
parsed = value;
}
@@ -178,7 +181,7 @@ function parameterValueList(
if (isNaN(parsed)) {
warnings.push({
message: "Invalid value attribute",
parameter: param
parameter: param,
});
parsed = value;
}
@@ -198,8 +201,8 @@ function GetParameterNamesResponse(xml): CpeGetResponse {
return {
name: "GetParameterNamesResponse",
parameterList: parameterInfoList(
xml.children.find(n => n.localName === "ParameterList")
)
xml.children.find((n) => n.localName === "ParameterList")
),
};
}
@@ -207,7 +210,7 @@ function GetParameterValues(methodRequest): string {
return `<cwmp:GetParameterValues><ParameterNames soap-enc:arrayType="xsd:string[${
methodRequest.parameterNames.length
}]">${methodRequest.parameterNames
.map(p => `<string>${p}</string>`)
.map((p) => `<string>${p}</string>`)
.join("")}</ParameterNames></cwmp:GetParameterValues>`;
}
@@ -215,13 +218,13 @@ function GetParameterValuesResponse(xml: Element): CpeGetResponse {
return {
name: "GetParameterValuesResponse",
parameterList: parameterValueList(
xml.children.find(n => n.localName === "ParameterList")
)
xml.children.find((n) => n.localName === "ParameterList")
),
};
}
function SetParameterValues(methodRequest): string {
const params = methodRequest.parameterList.map(p => {
const params = methodRequest.parameterList.map((p) => {
let val = p[1];
if (p[2] === "xsd:dateTime" && typeof val === "number") {
val = new Date(val).toISOString();
@@ -237,24 +240,24 @@ function SetParameterValues(methodRequest): string {
return `<cwmp:SetParameterValues><ParameterList soap-enc:arrayType="cwmp:ParameterValueStruct[${
methodRequest.parameterList.length
}]">${params.join(
""
)}</ParameterList><ParameterKey>${methodRequest.parameterKey ||
""}</ParameterKey></cwmp:SetParameterValues>`;
}]">${params.join("")}</ParameterList><ParameterKey>${
methodRequest.parameterKey || ""
}</ParameterKey></cwmp:SetParameterValues>`;
}
function SetParameterValuesResponse(xml: Element): CpeSetResponse {
return {
name: "SetParameterValuesResponse",
status: parseInt(xml.children.find(n => n.localName === "Status").text)
status: parseInt(xml.children.find((n) => n.localName === "Status").text),
};
}
function AddObject(methodRequest): string {
return `<cwmp:AddObject><ObjectName>${
methodRequest.objectName
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
""}</ParameterKey></cwmp:AddObject>`;
}</ObjectName><ParameterKey>${
methodRequest.parameterKey || ""
}</ParameterKey></cwmp:AddObject>`;
}
function AddObjectResponse(xml: Element): CpeSetResponse {
@@ -273,32 +276,34 @@ function AddObjectResponse(xml: Element): CpeSetResponse {
return {
name: "AddObjectResponse",
instanceNumber: instanceNumber,
status: status
status: status,
};
}
function DeleteObject(methodRequest): string {
return `<cwmp:DeleteObject><ObjectName>${
methodRequest.objectName
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
""}</ParameterKey></cwmp:DeleteObject>`;
}</ObjectName><ParameterKey>${
methodRequest.parameterKey || ""
}</ParameterKey></cwmp:DeleteObject>`;
}
function DeleteObjectResponse(xml: Element): CpeSetResponse {
return {
name: "DeleteObjectResponse",
status: parseInt(xml.children.find(n => n.localName === "Status").text)
status: parseInt(xml.children.find((n) => n.localName === "Status").text),
};
}
function Reboot(methodRequest): string {
return `<cwmp:Reboot><CommandKey>${methodRequest.commandKey ||
""}</CommandKey></cwmp:Reboot>`;
return `<cwmp:Reboot><CommandKey>${
methodRequest.commandKey || ""
}</CommandKey></cwmp:Reboot>`;
}
function RebootResponse(): CpeSetResponse {
return {
name: "RebootResponse"
name: "RebootResponse",
};
}
@@ -308,23 +313,26 @@ function FactoryReset(): string {
function FactoryResetResponse(): CpeSetResponse {
return {
name: "FactoryResetResponse"
name: "FactoryResetResponse",
};
}
function Download(methodRequest): string {
return `<cwmp:Download><CommandKey>${methodRequest.commandKey ||
""}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
return `<cwmp:Download><CommandKey>${
methodRequest.commandKey || ""
}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
methodRequest.url
}</URL><Username>${encodeEntities(
methodRequest.username || ""
)}</Username><Password>${encodeEntities(
methodRequest.password || ""
)}</Password><FileSize>${methodRequest.fileSize ||
"0"}</FileSize><TargetFileName>${encodeEntities(
)}</Password><FileSize>${
methodRequest.fileSize || "0"
}</FileSize><TargetFileName>${encodeEntities(
methodRequest.targetFileName || ""
)}</TargetFileName><DelaySeconds>${methodRequest.delaySeconds ||
"0"}</DelaySeconds><SuccessURL>${encodeEntities(
)}</TargetFileName><DelaySeconds>${
methodRequest.delaySeconds || "0"
}</DelaySeconds><SuccessURL>${encodeEntities(
methodRequest.successUrl || ""
)}</SuccessURL><FailureURL>${encodeEntities(
methodRequest.failureUrl || ""
@@ -351,7 +359,7 @@ function DownloadResponse(xml: Element): CpeSetResponse {
name: "DownloadResponse",
status: status,
startTime: startTime,
completeTime: completeTime
completeTime: completeTime,
};
}
@@ -361,7 +369,7 @@ function Inform(xml: Element): InformRequest {
Manufacturer: null,
OUI: null,
ProductClass: null,
SerialNumber: null
SerialNumber: null,
};
for (const c of xml.children) {
@@ -389,7 +397,7 @@ function Inform(xml: Element): InformRequest {
parameterList: parameterList,
deviceId: deviceId,
event: evnt,
retryCount: retryCount
retryCount: retryCount,
};
}
@@ -405,7 +413,7 @@ function GetRPCMethodsResponse(methodResponse): string {
return `<cwmp:GetRPCMethodsResponse><MethodList soap-enc:arrayType="xsd:string[${
methodResponse.methodList.length
}]">${methodResponse.methodList
.map(m => `<string>${m}</string>`)
.map((m) => `<string>${m}</string>`)
.join("")}</MethodList></cwmp:GetRPCMethodsResponse>`;
}
@@ -433,7 +441,7 @@ function TransferComplete(xml: Element): TransferCompleteRequest {
commandKey: commandKey,
faultStruct: _faultStruct,
startTime: startTime,
completeTime: completeTime
completeTime: completeTime,
};
}
@@ -444,7 +452,7 @@ function TransferCompleteResponse(): string {
function RequestDownload(xml: Element): CpeRequest {
return {
name: "RequestDownload",
fileType: xml.children.find(n => n.localName === "FileType").text
fileType: xml.children.find((n) => n.localName === "FileType").text,
};
}
@@ -480,7 +488,7 @@ function faultStruct(xml: Element): FaultStruct {
setParameterValuesFault.push({
parameterName: pn,
faultCode: fc,
faultString: fs
faultString: fs,
});
}
}
@@ -499,7 +507,7 @@ function fault(xml: Element): CpeFault {
faultString = decodeEntities(c.text);
break;
case "detail":
detail = faultStruct(c.children.find(n => n.localName === "Fault"));
detail = faultStruct(c.children.find((n) => n.localName === "Fault"));
break;
}
}
@@ -507,7 +515,11 @@ function fault(xml: Element): CpeFault {
return { faultCode, faultString, detail };
}
export function request(body: string, cwmpVersion, warn): SoapMessage {
export function request(
body: string,
cwmpVersion: string,
warn: Record<string, unknown>[]
): SoapMessage {
warnings = warn;
const rpc = {
@@ -516,7 +528,7 @@ export function request(body: string, cwmpVersion, warn): SoapMessage {
sessionTimeout: null,
cpeRequest: null,
cpeFault: null,
cpeResponse: null
cpeResponse: null,
};
if (!body.length) return rpc;
@@ -563,9 +575,9 @@ export function request(body: string, cwmpVersion, warn): SoapMessage {
const attrs = memoizedParseAttrs(e.attrs);
const attr = namespace
? attrs.find(
s => s.namespace === "xmlns" && s.localName === namespace
(s) => s.namespace === "xmlns" && s.localName === namespace
)
: attrs.find(s => s.name === "xmlns");
: attrs.find((s) => s.name === "xmlns");
if (attr) namespaceHref = attr.value;
}
@@ -653,13 +665,18 @@ const namespacesAttrs = {
.join(" "),
"1.4": Object.entries(NAMESPACES["1.4"])
.map(([k, v]) => `xmlns:${k}="${v}"`)
.join(" ")
.join(" "),
};
export function response(rpc): { code: number; headers: {}; data: string } {
export function response(rpc: {
id: string;
acsRequest?: AcsRequest;
acsResponse?: AcsResponse;
cwmpVersion?: string;
}): { code: number; headers: Record<string, string>; data: string } {
const headers = {
Server: SERVER_NAME,
SOAPServer: SERVER_NAME
SOAPServer: SERVER_NAME,
};
if (!rpc) return { code: 204, headers: headers, data: "" };
@@ -721,6 +738,6 @@ export function response(rpc): { code: number; headers: {}; data: string } {
namespacesAttrs[rpc.cwmpVersion]
}><soap-env:Header><cwmp:ID soap-env:mustUnderstand="1">${
rpc.id
}</cwmp:ID></soap-env:Header><soap-env:Body>${body}</soap-env:Body></soap-env:Envelope>`
}</cwmp:ID></soap-env:Header><soap-env:Body>${body}</soap-env:Body></soap-env:Envelope>`,
};
}
+8 -8
View File
@@ -152,7 +152,7 @@ export interface SessionContext {
}
export interface Task {
_id: string;
_id?: string;
name: string;
parameterNames?: string[];
parameterValues?: [string, string | number | boolean, string?][];
@@ -300,7 +300,7 @@ export interface QueryOptions {
export interface Declaration {
path: Path;
pathGet: number;
pathSet?: [number, number];
pathSet?: number | [number, number];
attrGet?: { object?: number; writable?: number; value?: number };
attrSet?: {
object?: boolean;
@@ -322,7 +322,7 @@ export interface Preset {
channel: string;
schedule?: { md5: string; duration: number; schedule: any };
events?: { [event: string]: boolean };
precondition?: {};
precondition?: Expression;
provisions: string[][];
}
@@ -367,12 +367,12 @@ export interface Config {
}
export interface UiConfig {
filters: {};
device: {};
index: {};
filters: Record<string, unknown>;
device: Record<string, unknown>;
index: Record<string, unknown>;
overview: {
charts?: {};
groups?: {};
charts?: Record<string, unknown>;
groups?: Record<string, unknown>;
};
pageSize?: Expression;
}
+13 -13
View File
@@ -52,14 +52,14 @@ const getAuthorizer = memoize(
(snapshot: string, rolesStr: string): Authorizer => {
const roles: string[] = JSON.parse(rolesStr);
const allPermissions = localCache.getPermissions(snapshot);
const permissionSets: PermissionSet[] = roles.map(r =>
const permissionSets: PermissionSet[] = roles.map((r) =>
Object.values(allPermissions[r] || {})
);
return new Authorizer(permissionSets);
}
);
koa.on("error", async err => {
koa.on("error", async (err) => {
throw err;
});
@@ -84,7 +84,7 @@ koa.use(
}
return true;
}
},
})
);
@@ -111,7 +111,7 @@ koa.use(async (ctx, next) => {
return next();
});
router.post("/login", async ctx => {
router.post("/login", async (ctx) => {
if (!JWT_SECRET) {
ctx.status = 500;
ctx.body = "UI_JWT_SECRET is not set";
@@ -126,7 +126,7 @@ router.post("/login", async ctx => {
message: "Log in",
context: ctx,
username: username,
method: null
method: null,
};
function success(authMethod): void {
@@ -150,13 +150,13 @@ router.post("/login", async ctx => {
failure();
});
router.post("/logout", async ctx => {
router.post("/logout", async (ctx) => {
ctx.cookies.set(JWT_COOKIE); // Delete cookie
ctx.body = "";
logger.accessInfo({
message: "Log out",
context: ctx
context: ctx,
});
});
@@ -170,11 +170,11 @@ koa.use(async (ctx, next) => {
koa.use(koaBodyParser());
router.use("/api", api.routes(), api.allowedMethods());
router.get("/status", ctx => {
router.get("/status", (ctx) => {
ctx.body = "OK";
});
router.get("/init", async ctx => {
router.get("/init", async (ctx) => {
const status = await init.getStatus();
if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) {
if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false;
@@ -195,7 +195,7 @@ router.get("/init", async ctx => {
ctx.body = status;
});
router.post("/init", async ctx => {
router.post("/init", async (ctx) => {
const status = ctx.request.body;
if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) {
if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false;
@@ -216,7 +216,7 @@ router.post("/init", async ctx => {
ctx.body = "";
});
router.get("/", async ctx => {
router.get("/", async (ctx) => {
const permissionSets: PermissionSet[] = ctx.state.authorizer.getPermissionSets();
let wizard = "";
@@ -234,7 +234,7 @@ router.get("/", async ctx => {
<noscript>GenieACS UI requires JavaScript to work. Please enable JavaScript in your browser.</noscript>
<script>
window.clientConfig = ${JSON.stringify({
ui: localCache.getUiConfig(ctx.state.configSnapshot)
ui: localCache.getUiConfig(ctx.state.configSnapshot),
})};
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
window.genieacsVersion = ${JSON.stringify(VERSION)};
@@ -251,7 +251,7 @@ router.get("/", async ctx => {
koa.use(
koaCompress({
flush: Z_SYNC_FLUSH
flush: Z_SYNC_FLUSH,
})
);
+30 -17
View File
@@ -26,8 +26,9 @@ import {
insertTasks,
watchTask,
connectionRequest,
deleteDevice
deleteDevice,
} from "../api-functions";
import { Task } from "../types";
async function deleteFault(id): Promise<void> {
const deviceId = id.split(":", 1)[0];
@@ -37,13 +38,16 @@ async function deleteFault(id): Promise<void> {
db.deleteFault(id),
channel.startsWith("task_")
? db.deleteTask(new ObjectID(channel.slice(5)))
: null
: null,
]);
await del(`${deviceId}_tasks_faults_operations`);
}
export async function deleteResource(resource, id): Promise<void> {
export async function deleteResource(
resource: string,
id: string
): Promise<void> {
switch (resource) {
case "devices":
await deleteDevice(id);
@@ -86,18 +90,18 @@ export async function deleteResource(resource, id): Promise<void> {
}
export async function postTasks(
deviceId,
tasks,
timeout,
device
deviceId: string,
tasks: Task[],
timeout: number,
device: Record<string, { value?: [boolean | number | string, string] }>
): Promise<{ connectionRequest: string; tasks: any[] }> {
for (const task of tasks) {
delete task._id;
task.device = deviceId;
task["device"] = deviceId;
}
tasks = await insertTasks(tasks);
const statuses = tasks.map(t => {
const statuses = tasks.map((t) => {
return { _id: t._id, status: "pending" };
});
@@ -106,9 +110,10 @@ export async function postTasks(
try {
await connectionRequest(deviceId, device);
} catch (err) {
for (const t of statuses) db.deleteTask(new ObjectID(t._id));
return {
connectionRequest: err.message,
tasks: statuses
tasks: statuses,
};
}
@@ -131,9 +136,9 @@ export async function postTasks(
r.status = "done";
} else if (res[i * 2 + 1].length === 1) {
r.status = "fault";
r.fault = res[i * 2 + 1][0];
r["fault"] = res[i * 2 + 1][0];
}
db.deleteTask(r._id);
db.deleteTask(new ObjectID(r._id));
}
return { connectionRequest: "OK", tasks: statuses };
@@ -149,7 +154,11 @@ interface PingResponse {
mdev: number;
}
export async function putResource(resource, id, data): Promise<void> {
export async function putResource(
resource: string,
id: string,
data: Record<string, unknown>
): Promise<void> {
if (resource === "presets") {
await db.putPreset(id, data);
} else if (resource === "provisions") {
@@ -161,21 +170,25 @@ export async function putResource(resource, id, data): Promise<void> {
} else if (resource === "permissions") {
await db.putPermission(id, data);
} else if (resource === "users") {
delete data.password;
delete data.salt;
delete data["password"];
delete data["salt"];
await db.putUser(id, data);
}
await del("presets_hash");
}
export function authLocal(snapshot, username, password): Promise<boolean> {
export function authLocal(
snapshot: string,
username: string,
password: string
): Promise<boolean> {
return new Promise((resolve, reject) => {
const users = getUsers(snapshot);
const user = users[username];
if (!user || !user.password) return void resolve(null);
hashPassword(password, user.salt)
.then(hash => {
.then((hash) => {
if (hash === user.password) resolve(true);
else resolve(false);
})
+41 -39
View File
@@ -53,7 +53,7 @@ const RESOURCE_IDS = {
permissions: "_id",
users: "_id",
faults: "_id",
tasks: "_id"
tasks: "_id",
};
const resources = {
@@ -66,21 +66,21 @@ const resources = {
permissions: 0 | RESOURCE_DELETE | RESOURCE_PUT,
users: 0 | RESOURCE_DELETE | RESOURCE_PUT,
faults: 0 | RESOURCE_DELETE,
tasks: 0
tasks: 0,
};
router.get(`/devices/:id.csv`, async ctx => {
router.get(`/devices/:id.csv`, async (ctx) => {
const authorizer: Authorizer = ctx.state.authorizer;
const log = {
message: "Query device (CSV)",
context: ctx,
id: ctx.params.id
id: ctx.params.id,
};
const filter = and(authorizer.getFilter("devices", 2), [
"=",
["PARAM", RESOURCE_IDS.devices],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess("devices", 2)) {
@@ -113,9 +113,9 @@ router.get(`/devices/:id.csv`, async ctx => {
p.writableTimestamp,
p.value != null ? `"${p.value[0].toString().replace(/"/g, '""')}"` : "",
p.value != null ? p.value[1] : "",
p.valueTimestamp
p.valueTimestamp,
];
ctx.body.write(row.map(r => (r != null ? r : "")).join(",") + "\n");
ctx.body.write(row.map((r) => (r != null ? r : "")).join(",") + "\n");
}
ctx.body.end();
logger.accessInfo(log);
@@ -132,7 +132,7 @@ for (const [resource, flags] of Object.entries(resources)) {
message: `Count ${resource}`,
context: ctx,
filter: ctx.request.query.filter,
count: null
count: null,
};
if (!authorizer.hasAccess(resource, 1)) {
@@ -144,7 +144,7 @@ for (const [resource, flags] of Object.entries(resources)) {
if (resource === "tasks" || resource === "faults") {
filter = and(filter, [
"NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000]
["<", ["PARAM", "expiry"], Date.now() + 60000],
]);
}
@@ -180,7 +180,7 @@ for (const [resource, flags] of Object.entries(resources)) {
limit: options.limit,
skip: options.skip,
sort: options.sort,
projection: options.projection
projection: options.projection,
};
if (!authorizer.hasAccess(resource, 2)) {
@@ -192,7 +192,7 @@ for (const [resource, flags] of Object.entries(resources)) {
if (resource === "tasks" || resource === "faults") {
filter = and(filter, [
"NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000]
["<", ["PARAM", "expiry"], Date.now() + 60000],
]);
}
@@ -201,7 +201,7 @@ for (const [resource, flags] of Object.entries(resources)) {
let c = 0;
ctx.body.write("[\n");
await db.query(resource, filter, options, obj => {
await db.query(resource, filter, options, (obj) => {
ctx.body.write((c++ ? "," : "") + JSON.stringify(obj) + "\n");
});
ctx.body.end("]");
@@ -227,7 +227,7 @@ for (const [resource, flags] of Object.entries(resources)) {
filter: ctx.request.query.filter,
limit: options.limit,
skip: options.skip,
sort: options.sort
sort: options.sort,
};
if (!authorizer.hasAccess(resource, 2)) {
@@ -235,7 +235,9 @@ for (const [resource, flags] of Object.entries(resources)) {
return void next();
}
const columns = JSON.parse(ctx.request.query.columns);
const columns: Record<string, Expression> = JSON.parse(
ctx.request.query.columns
);
const now = Date.now();
for (const [k, v] of Object.entries(columns)) {
@@ -249,7 +251,7 @@ for (const [resource, flags] of Object.entries(resources)) {
if (resource === "tasks" || resource === "faults") {
filter = and(filter, [
"NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000]
["<", ["PARAM", "expiry"], Date.now() + 60000],
]);
}
@@ -260,11 +262,11 @@ for (const [resource, flags] of Object.entries(resources)) {
);
ctx.body.write(
Object.keys(columns).map(k => `"${k.replace(/"/, '""')}"`) + "\n"
Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) + "\n"
);
await db.query(resource, filter, options, obj => {
const arr = Object.values(columns).map(exp => {
const v = evaluate(exp, obj, null, e => {
await db.query(resource, filter, options, (obj) => {
const arr = Object.values(columns).map((exp) => {
const v = evaluate(exp, obj, null, (e) => {
if (Array.isArray(e)) {
if (e[0] === "PARAM") {
if (resource === "devices") {
@@ -303,13 +305,13 @@ for (const [resource, flags] of Object.entries(resources)) {
const log = {
message: `Count ${resource}`,
context: ctx,
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`,
};
const filter = and(authorizer.getFilter(resource, 2), [
"=",
["PARAM", RESOURCE_IDS[resource]],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess(resource, 2)) {
logUnauthorizedWarning(log);
@@ -329,13 +331,13 @@ for (const [resource, flags] of Object.entries(resources)) {
const log = {
message: `Query ${resource}`,
context: ctx,
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`,
};
const filter = and(authorizer.getFilter(resource, 2), [
"=",
["PARAM", RESOURCE_IDS[resource]],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess(resource, 2)) {
logUnauthorizedWarning(log);
@@ -356,13 +358,13 @@ for (const [resource, flags] of Object.entries(resources)) {
const log = {
message: `Delete ${resource}`,
context: ctx,
id: ctx.params.id
id: ctx.params.id,
};
const filter = and(authorizer.getFilter(resource, 3), [
"=",
["PARAM", RESOURCE_IDS[resource]],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess(resource, 3)) {
logUnauthorizedWarning(log);
@@ -393,7 +395,7 @@ for (const [resource, flags] of Object.entries(resources)) {
const log = {
message: `Put ${resource}`,
context: ctx,
id: id
id: id,
};
if (!authorizer.hasAccess(resource, 3)) {
@@ -434,7 +436,7 @@ router.put("/files/:id", async (ctx, next) => {
message: `Upload ${resource}`,
context: ctx,
id: id,
metadata: null
metadata: null,
};
if (!authorizer.hasAccess(resource, 3)) {
@@ -446,7 +448,7 @@ router.put("/files/:id", async (ctx, next) => {
fileType: ctx.request.headers["metadata.filetype"] || "",
oui: ctx.request.headers["metadata.oui"] || "",
productClass: ctx.request.headers["metadata.productclass"] || "",
version: ctx.request.headers["metadata.version"] || ""
version: ctx.request.headers["metadata.version"] || "",
};
const validate = authorizer.getValidator(resource, metadata);
@@ -474,13 +476,13 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
message: "Commit tasks",
context: ctx,
deviceId: ctx.params.id,
tasks: null
tasks: null,
};
const filter = and(authorizer.getFilter("devices", 3), [
"=",
["PARAM", "DeviceID.ID"],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess("devices", 3)) {
logUnauthorizedWarning(log);
@@ -503,7 +505,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
"cwmp.deviceOnlineThreshold",
{},
Date.now(),
exp => {
(exp) => {
if (!Array.isArray(exp)) return exp;
if (exp[0] === "PARAM") {
const p = device[exp[1]];
@@ -519,7 +521,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
}
return exp;
}
);
) as number;
const res = await apiFunctions.postTasks(
ctx.params.id,
@@ -528,7 +530,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
device
);
log.tasks = res.tasks.map(t => t._id).join(",");
log.tasks = res.tasks.map((t) => t._id).join(",");
logger.accessInfo(log);
@@ -542,13 +544,13 @@ router.post("/devices/:id/tags", async (ctx, next) => {
message: "Update tags",
context: ctx,
deviceId: ctx.params.id,
tags: ctx.request.body
tags: ctx.request.body,
};
const filter = and(authorizer.getFilter("devices", 3), [
"=",
["PARAM", "DeviceID.ID"],
ctx.params.id
ctx.params.id,
]);
if (!authorizer.hasAccess("devices", 3)) {
logUnauthorizedWarning(log);
@@ -577,8 +579,8 @@ router.post("/devices/:id/tags", async (ctx, next) => {
ctx.body = "";
});
router.get("/ping/:host", async ctx => {
return new Promise(resolve => {
router.get("/ping/:host", async (ctx) => {
return new Promise((resolve) => {
ping(ctx.params.host, (err, parsed) => {
if (parsed) {
ctx.body = parsed;
@@ -597,7 +599,7 @@ router.put("/users/:id/password", async (ctx, next) => {
const log = {
message: "Change password",
context: ctx,
username: username
username: username,
};
if (!ctx.state.user) {
@@ -622,7 +624,7 @@ router.put("/users/:id/password", async (ctx, next) => {
const filter = and(authorizer.getFilter("users", 3), [
"=",
["PARAM", RESOURCE_IDS.users],
username
username,
]);
const res = await db.query("users", filter);
if (!res.length) return void next();
+83 -50
View File
@@ -17,19 +17,20 @@
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
*/
import { MongoClient, GridFSBucket } from "mongodb";
import { MongoClient, GridFSBucket, ObjectID } from "mongodb";
import { Script } from "vm";
import * as config from "../config";
import * as mongodbFunctions from "../mongodb-functions";
import * as expression from "../common/expression";
import { QueryOptions } from "../types";
import { QueryOptions, Expression } from "../types";
import { Readable } from "stream";
const CACHE_TTL = 300000;
let clientPromise: Promise<MongoClient>;
const RESOURCE_COLLECTION = {
files: "fs.files"
files: "fs.files",
};
function ensureIndexes(client): void {
@@ -58,27 +59,31 @@ function getClient(): Promise<MongoClient> {
return clientPromise;
}
export function cache<T>(key, valueGetter: () => Promise<T>, ttl): Promise<T> {
export function cache<T>(
key: string,
valueGetter: () => Promise<T>,
ttl: number
): Promise<T> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const collection = client.db().collection("cache");
collection.findOne({ _id: key }, (err, doc) => {
if (err) return void reject(err);
if (doc != null) return void resolve(JSON.parse(doc.value) as T);
valueGetter()
.then(res => {
.then((res) => {
const expire = Date.now() + (ttl || CACHE_TTL);
const cacheDoc = {
_id: key,
value: JSON.stringify(res),
expire: new Date(expire)
expire: new Date(expire),
};
collection.updateOne(
{ _id: key },
{ $set: cacheDoc },
{ upsert: true },
err => {
(err) => {
if (err) reject(err);
else resolve(res);
}
@@ -92,8 +97,8 @@ export function cache<T>(key, valueGetter: () => Promise<T>, ttl): Promise<T> {
}
export function query(
resource,
filter,
resource: string,
filter: Expression,
options?: QueryOptions,
callback?: (doc: any) => void
): Promise<any[]> {
@@ -121,7 +126,7 @@ export function query(
}
return new Promise((resolve, reject) => {
getClient().then(client => {
getClient().then((client) => {
const collection = client
.db()
.collection(RESOURCE_COLLECTION[resource] || resource);
@@ -160,20 +165,20 @@ export function query(
cursor.toArray((err, docs) => {
if (err) return reject(err);
if (resource === "devices")
docs = docs.map(d => mongodbFunctions.flattenDevice(d));
docs = docs.map((d) => mongodbFunctions.flattenDevice(d));
else if (resource === "faults")
docs = docs.map(d => mongodbFunctions.flattenFault(d));
docs = docs.map((d) => mongodbFunctions.flattenFault(d));
else if (resource === "tasks")
docs = docs.map(d => mongodbFunctions.flattenTask(d));
docs = docs.map((d) => mongodbFunctions.flattenTask(d));
else if (resource === "presets")
docs = docs.map(d => mongodbFunctions.flattenPreset(d));
docs = docs.map((d) => mongodbFunctions.flattenPreset(d));
else if (resource === "files")
docs = docs.map(d => mongodbFunctions.flattenFile(d));
docs = docs.map((d) => mongodbFunctions.flattenFile(d));
return resolve(docs);
});
} else {
cursor.forEach(
doc => {
(doc) => {
if (resource === "devices")
doc = mongodbFunctions.flattenDevice(doc);
else if (resource === "faults")
@@ -186,7 +191,7 @@ export function query(
doc = mongodbFunctions.flattenFile(doc);
callback(doc);
},
err => {
(err) => {
if (err) reject(err);
else resolve();
}
@@ -196,7 +201,7 @@ export function query(
});
}
export function count(resource, filter): Promise<number> {
export function count(resource: string, filter: Expression): Promise<number> {
let q;
filter = expression.evaluate(filter, null, Date.now());
@@ -213,7 +218,7 @@ export function count(resource, filter): Promise<number> {
}
return new Promise((resolve, reject) => {
getClient().then(client => {
getClient().then((client) => {
const collection = client
.db()
.collection(RESOURCE_COLLECTION[resource] || resource);
@@ -225,7 +230,10 @@ export function count(resource, filter): Promise<number> {
});
}
export function updateDeviceTags(deviceId, tags): Promise<void> {
export function updateDeviceTags(
deviceId: string,
tags: Record<string, boolean>
): Promise<void> {
return new Promise((resolve, reject) => {
const add = [];
const pull = [];
@@ -242,13 +250,13 @@ export function updateDeviceTags(deviceId, tags): Promise<void> {
}
}
getClient()
.then(client => {
.then((client) => {
const collection = client.db().collection("devices");
const object = {};
if (add && add.length) object["$addToSet"] = { _tags: { $each: add } };
if (pull && pull.length) object["$pullAll"] = { _tags: pull };
collection.updateOne({ _id: deviceId }, object, err => {
collection.updateOne({ _id: deviceId }, object, (err) => {
if (err) return void reject(err);
resolve();
});
@@ -260,11 +268,11 @@ export function updateDeviceTags(deviceId, tags): Promise<void> {
function putResource(resource, id, object): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const collection = client
.db()
.collection(RESOURCE_COLLECTION[resource] || resource);
collection.replaceOne({ _id: id }, object, { upsert: true }, err => {
collection.replaceOne({ _id: id }, object, { upsert: true }, (err) => {
if (err) return void reject(err);
resolve();
});
@@ -273,14 +281,17 @@ function putResource(resource, id, object): Promise<void> {
});
}
function deleteResource(resource, id): Promise<void> {
function deleteResource(
resource: string,
id: string | ObjectID
): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const collection = client
.db()
.collection(RESOURCE_COLLECTION[resource] || resource);
collection.deleteOne({ _id: id }, err => {
collection.deleteOne({ _id: id }, (err) => {
if (err) return void reject(err);
resolve();
});
@@ -289,16 +300,22 @@ function deleteResource(resource, id): Promise<void> {
});
}
export function putPreset(id, object): Promise<void> {
export function putPreset(
id: string,
object: Record<string, unknown>
): Promise<void> {
object = mongodbFunctions.preProcessPreset(object);
return putResource("presets", id, object);
}
export function deletePreset(id): Promise<void> {
export function deletePreset(id: string): Promise<void> {
return deleteResource("presets", id);
}
export function putProvision(id, object): Promise<void> {
export function putProvision(
id: string,
object: Record<string, unknown>
): Promise<void> {
if (!object.script) object.script = "";
try {
new Script(`"use strict";(function(){\n${object.script}\n})();`);
@@ -308,11 +325,14 @@ export function putProvision(id, object): Promise<void> {
return putResource("provisions", id, object);
}
export function deleteProvision(id): Promise<void> {
export function deleteProvision(id: string): Promise<void> {
return deleteResource("provisions", id);
}
export function putVirtualParameter(id, object): Promise<void> {
export function putVirtualParameter(
id: string,
object: Record<string, unknown>
): Promise<void> {
if (!object.script) object.script = "";
try {
new Script(`"use strict";(function(){\n${object.script}\n})();`);
@@ -322,37 +342,46 @@ export function putVirtualParameter(id, object): Promise<void> {
return putResource("virtualParameters", id, object);
}
export function deleteVirtualParameter(id): Promise<void> {
export function deleteVirtualParameter(id: string): Promise<void> {
return deleteResource("virtualParameters", id);
}
export function putConfig(id, object): Promise<void> {
export function putConfig(
id: string,
object: Record<string, unknown>
): Promise<void> {
return putResource("config", id, object);
}
export function deleteConfig(id): Promise<void> {
export function deleteConfig(id: string): Promise<void> {
return deleteResource("config", id);
}
export function putPermission(id, object): Promise<void> {
export function putPermission(
id: string,
object: Record<string, unknown>
): Promise<void> {
return putResource("permissions", id, object);
}
export function deletePermission(id): Promise<void> {
export function deletePermission(id: string): Promise<void> {
return deleteResource("permissions", id);
}
export function putUser(id, object): Promise<void> {
export function putUser(
id: string,
object: Record<string, unknown>
): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const collection = client.db().collection("users");
// update instead of replace to keep the password if not set by user
collection.updateOne(
{ _id: id },
{ $set: object },
{ upsert: true },
err => {
(err) => {
if (err) return void reject(err);
resolve();
}
@@ -362,17 +391,21 @@ export function putUser(id, object): Promise<void> {
});
}
export function deleteUser(id): Promise<void> {
export function deleteUser(id: string): Promise<void> {
return deleteResource("users", id);
}
export function putFile(filename, metadata, contentStream): Promise<void> {
export function putFile(
filename: string,
metadata: Record<string, string>,
contentStream: Readable
): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const bucket = new GridFSBucket(client.db());
const uploadStream = bucket.openUploadStreamWithId(filename, filename, {
metadata: metadata
metadata: metadata,
});
uploadStream.on("error", reject);
contentStream.on("error", reject);
@@ -383,12 +416,12 @@ export function putFile(filename, metadata, contentStream): Promise<void> {
});
}
export function deleteFile(filename): Promise<void> {
export function deleteFile(filename: string): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
.then((client) => {
const bucket = new GridFSBucket(client.db());
bucket.delete(filename, err => {
bucket.delete(filename as any, (err) => {
if (err) return void reject(err);
resolve();
});
@@ -397,11 +430,11 @@ export function deleteFile(filename): Promise<void> {
});
}
export function deleteFault(id): Promise<void> {
export function deleteFault(id: string): Promise<void> {
return deleteResource("faults", id);
}
export function deleteTask(id): Promise<void> {
export function deleteTask(id: ObjectID): Promise<void> {
return deleteResource("tasks", id);
}
+2 -2
View File
@@ -46,7 +46,7 @@ export default class VersionedMap<K, V> {
return this._revision;
}
public set revision(rev) {
public set revision(rev: number) {
for (let i = this._sizeDiff.length; i <= rev; ++i)
this._sizeDiff[i] = this._sizeDiff[i - 1];
@@ -193,7 +193,7 @@ export default class VersionedMap<K, V> {
const last = v[l];
v.splice(revision, l - revision);
if (last === NONEXISTENT && !v.some(vv => vv !== NONEXISTENT))
if (last === NONEXISTENT && !v.some((vv) => vv !== NONEXISTENT))
this.map.delete(k);
}
}
+8 -8
View File
@@ -90,7 +90,7 @@ export function parseAttrs(string: string): Attribute[] {
name: name,
namespace: namespace,
localName: localName,
value: value
value: value,
};
attrs.push(e);
name = "";
@@ -128,8 +128,8 @@ export function parseAttrs(string: string): Attribute[] {
return attrs;
}
export function decodeEntities(string): string {
return string.replace(/&[0-9a-z#]+;/gi, match => {
export function decodeEntities(string: string): string {
return string.replace(/&[0-9a-z#]+;/gi, (match) => {
switch (match) {
case "&quot;":
return '"';
@@ -161,15 +161,15 @@ export function decodeEntities(string): string {
});
}
export function encodeEntities(string): string {
export function encodeEntities(string: string): string {
const entities = {
"&": "&amp;",
'"': "&quot;",
"'": "&apos;",
"<": "&lt;",
">": "&gt;"
">": "&gt;",
};
return string.replace(/[&"'<>]/g, m => entities[m]);
return string.replace(/[&"'<>]/g, (m) => entities[m]);
}
export function parseXml(string: string): Element {
@@ -186,7 +186,7 @@ export function parseXml(string: string): Element {
attrs: "",
text: "",
bodyIndex: 0,
children: []
children: [],
};
const stack: Element[] = [root];
@@ -330,7 +330,7 @@ export function parseXml(string: string): Element {
: "",
text: "",
bodyIndex: i + 1,
children: []
children: [],
};
parent.children.push(e);
if (!selfClosing) stack.push(e);