188 lines
5.1 KiB
TypeScript
188 lines
5.1 KiB
TypeScript
import * as crypto from "crypto";
|
|
import * as config from "../config";
|
|
import { collections } from "../db/db";
|
|
import * as expression from "../common/expression/util";
|
|
import { parse } from "../common/expression/parser";
|
|
import { Expression, Users, Permissions, Config, UiConfig } from "../types";
|
|
import { LocalCache } from "../local-cache";
|
|
|
|
interface Snapshot {
|
|
permissions: Permissions;
|
|
users: Users;
|
|
config: Config;
|
|
ui: UiConfig;
|
|
}
|
|
|
|
async function fetchPermissions(): Promise<[string, Permissions]> {
|
|
const perms = await collections.permissions.find().toArray();
|
|
perms.sort((a, b) => (a._id > b._id ? 1 : -1));
|
|
const h = crypto
|
|
.createHash("md5")
|
|
.update(JSON.stringify(perms))
|
|
.digest("hex");
|
|
const permissions: Permissions = {};
|
|
|
|
for (const p of perms) {
|
|
if (!permissions[p.role]) permissions[p.role] = {};
|
|
if (!permissions[p.role][p.access]) permissions[p.role][p.access] = {};
|
|
|
|
permissions[p.role][p.access][p.resource] = {
|
|
access: p.access,
|
|
filter: parse(p.filter || "true"),
|
|
};
|
|
if (p.validate)
|
|
permissions[p.role][p.access][p.resource].validate = parse(p.validate);
|
|
}
|
|
|
|
return [h, permissions];
|
|
}
|
|
|
|
async function fetchUsers(): Promise<[string, Users]> {
|
|
const _users = await collections.users.find().toArray();
|
|
_users.sort((a, b) => (a._id > b._id ? 1 : -1));
|
|
const h = crypto
|
|
.createHash("md5")
|
|
.update(JSON.stringify(_users))
|
|
.digest("hex");
|
|
const users = {};
|
|
|
|
for (const user of _users) {
|
|
users[user._id] = {
|
|
password: user.password,
|
|
salt: user.salt,
|
|
roles: user.roles.split(",").map((s) => s.trim()),
|
|
};
|
|
}
|
|
|
|
return [h, users];
|
|
}
|
|
|
|
async function fetchConfig(): Promise<[string, Config, UiConfig]> {
|
|
const conf = await collections.config.find().toArray();
|
|
conf.sort((a, b) => (a._id > b._id ? 1 : -1));
|
|
const h = crypto.createHash("md5").update(JSON.stringify(conf)).digest("hex");
|
|
|
|
const ui = {
|
|
filters: {},
|
|
device: {},
|
|
index: {},
|
|
overview: {},
|
|
pageSize: null,
|
|
};
|
|
|
|
const _config = {};
|
|
|
|
for (const c of conf) {
|
|
// Evaluate expressions to simplify them
|
|
const val = expression.evaluate(parse(c.value));
|
|
_config[c._id] = val;
|
|
if (c._id.startsWith("ui.")) {
|
|
const keys = c._id.split(".");
|
|
if (!(keys[1] in ui)) continue;
|
|
// remove the first key(ui)
|
|
keys.shift();
|
|
let ref = ui;
|
|
while (keys.length > 1) {
|
|
const k = keys.shift();
|
|
if (ref[k] == null || typeof ref[k] !== "object") ref[k] = {};
|
|
ref = ref[k];
|
|
}
|
|
ref[keys[0]] = val;
|
|
}
|
|
}
|
|
|
|
return [h, _config, ui];
|
|
}
|
|
|
|
const localCache = new LocalCache<Snapshot>("ui-local-cache-hash", refresh);
|
|
|
|
async function refresh(): Promise<[string, Snapshot]> {
|
|
const res = await Promise.all([
|
|
fetchPermissions(),
|
|
fetchUsers(),
|
|
fetchConfig(),
|
|
]);
|
|
|
|
const h = crypto.createHash("md5");
|
|
for (const r of res) h.update(r[0]);
|
|
|
|
const snapshot = {
|
|
permissions: res[0][1],
|
|
users: res[1][1],
|
|
config: res[2][1],
|
|
ui: res[2][2],
|
|
};
|
|
|
|
return [h.digest("hex"), snapshot];
|
|
}
|
|
|
|
export async function getRevision(): Promise<string> {
|
|
return await localCache.getRevision();
|
|
}
|
|
|
|
export function getConfig(
|
|
revision: string,
|
|
key: string,
|
|
context: Record<string, unknown>,
|
|
now: number,
|
|
cb?: (e: Expression) => Expression,
|
|
): string | number | boolean | null {
|
|
const snapshot = localCache.get(revision);
|
|
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
|
|
|
const oldOpts = {
|
|
"cwmp.downloadTimeout": "DOWNLOAD_TIMEOUT",
|
|
"cwmp.debug": "DEBUG",
|
|
"cwmp.retryDelay": "RETRY_DELAY",
|
|
"cwmp.sessionTimeout": "SESSION_TIMEOUT",
|
|
"cwmp.connectionRequestTimeout": "CONNECTION_REQUEST_TIMEOUT",
|
|
"cwmp.gpnNextLevel": "GPN_NEXT_LEVEL",
|
|
"cwmp.gpvBatchSize": "GPV_BATCH_SIZE",
|
|
"cwmp.cookiesPath": "COOKIES_PATH",
|
|
"cwmp.datetimeMilliseconds": "DATETIME_MILLISECONDS",
|
|
"cwmp.booleanLiteral": "BOOLEAN_LITERAL",
|
|
"cwmp.connectionRequestAllowBasicAuth":
|
|
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
|
|
"cwmp.maxCommitIterations": "MAX_COMMIT_ITERATIONS",
|
|
"cwmp.deviceOnlineThreshold": "DEVICE_ONLINE_THRESHOLD",
|
|
"cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT",
|
|
};
|
|
|
|
if (!(key in snapshot.config)) {
|
|
if (key in oldOpts) {
|
|
let id;
|
|
if (context?.["id"]) {
|
|
id = context["id"];
|
|
} else if (cb) {
|
|
id = cb(["PARAM", "DeviceID.ID"]);
|
|
if (Array.isArray(id)) id = null;
|
|
}
|
|
return config.get(oldOpts[key], id);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const v = expression.evaluate(snapshot.config[key], context, now, cb);
|
|
return Array.isArray(v) ? null : v;
|
|
}
|
|
|
|
export function getConfigExpression(revision: string, key: string): Expression {
|
|
const snapshot = localCache.get(revision);
|
|
return snapshot.config[key];
|
|
}
|
|
|
|
export function getUsers(revision: string): Users {
|
|
const snapshot = localCache.get(revision);
|
|
return snapshot.users;
|
|
}
|
|
|
|
export function getPermissions(revision: string): Permissions {
|
|
const snapshot = localCache.get(revision);
|
|
return snapshot.permissions;
|
|
}
|
|
|
|
export function getUiConfig(revision: string): UiConfig {
|
|
const snapshot = localCache.get(revision);
|
|
return snapshot.ui;
|
|
}
|