"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DBHelper = void 0;
const Crypt_1 = require("./Crypt");
const openflow_api_1 = require("@openiap/openflow-api");
const Config_1 = require("./Config");
const Logger_1 = require("./Logger");
const Auth_1 = require("./Auth");
const LoginProvider_1 = require("./LoginProvider");
const cacheManager = require("cache-manager");
// var cacheManager = require('cache-manager');
var redisStore = require('cache-manager-ioredis');
var mongoStore = require('@skadefro/cache-manager-mongodb');
class DBHelper {
    constructor() {
        this.item_cache = null;
    }
    async init() {
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(this.memoryCache))
            return;
        this.mongoCache = cacheManager.caching({
            store: mongoStore,
            uri: Config_1.Config.mongodb_url,
            options: {
                collection: "_cache",
                compression: false,
                poolSize: 5
            }
        });
        if (Config_1.Config.cache_store_type == "mongodb") {
            this.memoryCache = this.mongoCache;
            this.ensureotel();
            return;
        }
        else if (Config_1.Config.cache_store_type == "redis") {
            this.memoryCache = cacheManager.caching({
                store: redisStore,
                host: Config_1.Config.cache_store_redis_host,
                port: Config_1.Config.cache_store_redis_port,
                password: Config_1.Config.cache_store_redis_password,
                ignoreCacheErrors: true,
                db: 0,
                ttl: Config_1.Config.cache_store_ttl_seconds,
                max: Config_1.Config.cache_store_max
            });
            // listen for redis connection error event
            var redisClient = this.memoryCache.store.getClient();
            redisClient.on('error', (error) => {
                Logger_1.Logger.instanse.error("DBHelper", "init", error);
            });
            this.ensureotel();
            return;
        }
        this.memoryCache = cacheManager.caching({
            store: 'memory',
            ignoreCacheErrors: true,
            max: Config_1.Config.cache_store_max,
            ttl: Config_1.Config.cache_store_ttl_seconds
        });
        this.ensureotel();
    }
    async clearCache(reason) {
        await this.init();
        var keys;
        if (Config_1.Config.cache_store_type == "redis") {
            keys = await this.memoryCache.keys('*');
        }
        else {
            keys = await this.memoryCache.keys();
        }
        for (var i = 0; i < keys.length; i++) {
            if (keys[i] && !keys[i].startsWith("requesttoken")) {
                this.memoryCache.del(keys[i]);
            }
        }
        Logger_1.Logger.instanse.debug("DBHelper", "clearCache", "clearCache called with reason: " + reason);
    }
    async DeleteKey(key) {
        await this.init();
        Logger_1.Logger.instanse.debug("DBHelper", "DeleteKey", "Remove from cache : " + key);
        await this.memoryCache.del(key);
    }
    ensureotel() {
        var _a;
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(Logger_1.Logger.otel) && !openflow_api_1.NoderedUtil.IsNullUndefinded(Logger_1.Logger.otel.meter) && openflow_api_1.NoderedUtil.IsNullUndefinded(this.item_cache)) {
            this.item_cache = Logger_1.Logger.otel.meter.createObservableGauge("openflow_item_cache_count", {
                description: 'Total number of cached items'
            });
            (_a = this.item_cache) === null || _a === void 0 ? void 0 : _a.addCallback(async (res) => {
                var keys = null;
                try {
                    if (Config_1.Config.cache_store_type == "redis") {
                        keys = await this.memoryCache.keys('*');
                    }
                    else {
                        keys = await this.memoryCache.keys();
                    }
                }
                catch (error) {
                }
                if (keys != null) {
                    res.observe(keys.length, { ...Logger_1.Logger.otel.defaultlabels, type: Config_1.Config.cache_store_type });
                }
                else {
                    res.observe(0, { ...Logger_1.Logger.otel.defaultlabels, type: Config_1.Config.cache_store_type });
                }
            });
        }
    }
    async FindById(_id, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(_id))
                return null;
            let item = await this.memoryCache.wrap("users" + _id, () => {
                Logger_1.Logger.instanse.debug("DBHelper", "FindById", "Add user to cache : " + _id);
                return Config_1.Config.db.getbyid(_id, "users", Crypt_1.Crypt.rootToken(), true, span);
            });
            this.ensureotel();
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            var res2 = await this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
            return res2;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async GetProviders(parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.GetProviders", parent);
        try {
            let items = await this.memoryCache.wrap("providers", () => {
                return Config_1.Config.db.query({ query: { _type: "provider" }, top: 10, collectionname: "config", jwt: Crypt_1.Crypt.rootToken() }, span);
                ;
            });
            // const result: Provider[] = [];
            // https://www.w3schools.com/icons/fontawesome5_icons_brands.asp
            items.forEach(provider => {
                // const item: any = { name: provider.name, id: provider.id, provider: provider.provider, logo: "fa-question-circle" };
                provider.logo = "fa-microsoft";
                if (provider.provider === "oidc") {
                    provider.logo = "fa-openid";
                }
                if (provider.provider === "google") {
                    provider.logo = "fa-google";
                }
                if (provider.provider === "saml") {
                    provider.logo = "fa-windows";
                }
                //result.push(item);
            });
            if (items.length === 0) {
                const item = { name: "Local", id: "local", provider: "local", logo: "fa-question-circle" };
                items.push(item);
            }
            return items;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async ClearProviders() {
        await this.memoryCache.del("providers");
    }
    async FindRequestTokenID(key, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindRequestTokenID", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(key))
                return null;
            if (Config_1.Config.cache_store_type == "redis") {
                return await this.memoryCache.get("requesttoken" + key);
            }
            else {
                return await this.mongoCache.get("requesttoken" + key);
            }
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async AdddRequestTokenID(key, data, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindRequestTokenID", parent);
        try {
            if (Config_1.Config.cache_store_type == "redis") {
                return await this.memoryCache.set("requesttoken" + key, data);
            }
            else {
                return await this.mongoCache.set("requesttoken" + key, data);
            }
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async RemoveRequestTokenID(key, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindRequestTokenID", parent);
        try {
            if (Config_1.Config.cache_store_type == "redis") {
                return await this.memoryCache.del("requesttoken" + key);
            }
            else {
                return await this.mongoCache.del("requesttoken" + key);
            }
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindByAuthorization(authorization, jwt, parent) {
        if (!openflow_api_1.NoderedUtil.IsNullEmpty(authorization) && authorization.indexOf(" ") > 1 &&
            (authorization.toLocaleLowerCase().startsWith("bearer") || authorization.toLocaleLowerCase().startsWith("jwt"))) {
            const token = authorization.split(" ")[1];
            let item = await this.memoryCache.wrap(token, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindByAuthorization", "Add authentication header to cache");
                return LoginProvider_1.LoginProvider.validateToken(token, parent);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), parent);
        }
        const b64auth = (authorization || '').split(' ')[1] || '';
        // const [login, password] = new Buffer(b64auth, 'base64').toString().split(':')
        const [login, password] = Buffer.from(b64auth, "base64").toString().split(':');
        if (!openflow_api_1.NoderedUtil.IsNullEmpty(login) && !openflow_api_1.NoderedUtil.IsNullEmpty(password)) {
            let item = await this.memoryCache.wrap(b64auth, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindByAuthorization", "Add basicauth header to cache");
                return Auth_1.Auth.ValidateByPassword(login, password, parent);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), parent);
        }
    }
    async FindQueueById(_id, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(_id))
                return null;
            let item = await this.memoryCache.wrap("mq" + _id, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindQueueById", "Add queue to cache : " + _id);
                return Config_1.Config.db.getbyid(_id, "mq", jwt, true, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindQueueByName(name, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(name))
                return null;
            let item = await this.memoryCache.wrap("queuename_" + name, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindQueueByName", "Add queue to cache : " + name);
                return Config_1.Config.db.GetOne({ query: { name }, collectionname: "mq", jwt }, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindExchangeById(_id, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(_id))
                return null;
            let item = await this.memoryCache.wrap("mq" + _id, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindExchangeById", "Add exchange to cache : " + _id);
                return Config_1.Config.db.getbyid(_id, "mq", jwt, true, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindExchangeByName(name, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(name))
                return null;
            let item = await this.memoryCache.wrap("exchangename_" + name, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindExchangeByName", "Add exchange to cache : " + name);
                return Config_1.Config.db.GetOne({ query: { name }, collectionname: "mq", jwt }, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindRoleById(_id, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindById", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(_id))
                return null;
            let item = await this.memoryCache.wrap("users" + _id, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindRoleById", "Add role to cache : " + _id);
                return Config_1.Config.db.getbyid(_id, "users", jwt, true, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return openflow_api_1.Role.assign(item);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindByUsername(username, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindByUsername", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(username))
                return null;
            let item = await this.memoryCache.wrap("username_" + username, () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindByUsername", "Add user to cache : " + username);
                return Config_1.Config.db.getbyusername(username, null, jwt, true, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async GetDisposableDomain(domain, parent) {
        await this.init();
        if (domain.indexOf("@") > -1) {
            domain = domain.substring(domain.indexOf("@") + 1);
        }
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindByUsername", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(domain))
                return null;
            let item = await this.memoryCache.wrap("disposable_" + domain, () => {
                const jwt = Crypt_1.Crypt.rootToken();
                Logger_1.Logger.instanse.debug("DBHelper", "IsDisposableDomain", "Add to cache : " + domain);
                const query = { name: domain, "_type": "disposable" };
                return Config_1.Config.db.GetOne({ query, collectionname: "domains", jwt }, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return item;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async FindByUsernameOrFederationid(username, issuer, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindByUsername", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullEmpty(username))
                return null;
            let item = await this.memoryCache.wrap("federation_" + username, () => {
                const jwt = Crypt_1.Crypt.rootToken();
                Logger_1.Logger.instanse.debug("DBHelper", "FindByUsername", "Add user to cache : " + username);
                return Config_1.Config.db.getbyusername(username, issuer, jwt, true, span);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return this.DecorateWithRoles(openflow_api_1.User.assign(item), span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async DecorateWithRoles(user, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.DecorateWithRoles", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(user))
                return null;
            if (!Config_1.Config.decorate_roles_fetching_all_roles) {
                if (!user.roles)
                    user.roles = [];
                const results = await this.memoryCache.wrap("userroles_" + user._id, () => {
                    Logger_1.Logger.instanse.debug("DBHelper", "DecorateWithRoles", "Add userroles to cache : " + user.name);
                    const pipe = [{ "$match": { "_id": user._id } },
                        {
                            "$graphLookup": {
                                from: "users",
                                startWith: "$_id",
                                connectFromField: "_id",
                                connectToField: "members._id",
                                as: "roles",
                                maxDepth: Config_1.Config.max_recursive_group_depth,
                                depthField: "depth",
                                restrictSearchWithMatch: { "_type": "role" }
                                // , "_id": { $nin: Config.db.WellknownIdsArray }, "members._id": { $nin: Config.db.WellknownIdsArray }
                            }
                        }, {
                            "$graphLookup": {
                                from: "users",
                                startWith: "$_id",
                                connectFromField: "members._id",
                                connectToField: "members._id",
                                as: "roles2",
                                maxDepth: 0,
                                depthField: "depth",
                                restrictSearchWithMatch: { "_type": "role" }
                            }
                        },
                        {
                            "$project": {
                                _id: 1,
                                name: 1,
                                username: 1,
                                roles: {
                                    $map: {
                                        input: "$roles",
                                        as: "roles",
                                        in: {
                                            "name": "$$roles.name",
                                            "_id": "$$roles._id"
                                        }
                                    }
                                },
                                roles2: {
                                    $map: {
                                        input: "$roles2",
                                        as: "roles2",
                                        in: {
                                            "name": "$$roles2.name",
                                            "_id": "$$roles2._id"
                                        }
                                    }
                                }
                            }
                        }
                    ];
                    return Config_1.Config.db.aggregate(pipe, "users", Crypt_1.Crypt.rootToken(), null, span);
                });
                if (results.length > 0) {
                    user.roles = [];
                    results[0].roles.forEach(r => {
                        const exists = user.roles.filter(x => x._id == r._id);
                        if (exists.length == 0)
                            user.roles.push(r);
                    });
                    results[0].roles2.forEach(r => {
                        const exists = user.roles.filter(x => x._id == r._id);
                        if (exists.length == 0)
                            user.roles.push(r);
                    });
                }
                let hasusers = user.roles.filter(x => x._id == openflow_api_1.WellknownIds.users);
                if (hasusers.length == 0) {
                    user.roles.push(new openflow_api_1.Rolemember("users", openflow_api_1.WellknownIds.users));
                }
                return user;
            }
            let cached_roles = await this.memoryCache.wrap("allroles", () => {
                Logger_1.Logger.instanse.debug("DBHelper", "DecorateWithRoles", "Add all roles");
                return Config_1.Config.db.query({ query: { _type: "role" }, projection: { "name": 1, "members": 1 }, top: Config_1.Config.expected_max_roles, collectionname: "users", jwt: Crypt_1.Crypt.rootToken() }, span);
            });
            if (cached_roles.length === 0 && user.username !== "root") {
                throw new Error("System has no roles !!!!!!");
            }
            user.roles = [];
            for (let role of cached_roles) {
                let isMember = -1;
                if (role.members !== undefined) {
                    isMember = role.members.map(function (e) { return e._id; }).indexOf(user._id);
                }
                if (isMember > -1) {
                    user.roles.push(new openflow_api_1.Rolemember(role.name, role._id));
                }
            }
            let hasusers = user.roles.filter(x => x._id == openflow_api_1.WellknownIds.users);
            if (hasusers.length == 0) {
                user.roles.push(new openflow_api_1.Rolemember("users", openflow_api_1.WellknownIds.users));
            }
            let updated = false;
            do {
                updated = false;
                for (let userrole of user.roles) {
                    for (let role of cached_roles) {
                        let isMember = -1;
                        if (role.members !== undefined) {
                            isMember = role.members.map(function (e) { return e._id; }).indexOf(userrole._id);
                        }
                        if (isMember > -1) {
                            const beenAdded = user.roles.map(function (e) { return e._id; }).indexOf(role._id);
                            if (beenAdded === -1) {
                                user.roles.push(new openflow_api_1.Rolemember(role.name, role._id));
                                updated = true;
                            }
                        }
                    }
                }
            } while (updated);
            user.roles.sort((a, b) => a.name.localeCompare(b.name));
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
        return user;
    }
    async FindRoleByName(name, jwt, parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.FindByUsername", parent);
        try {
            let item = await this.memoryCache.wrap("rolename_" + name, async () => {
                if (jwt === null || jwt == undefined || jwt == "") {
                    jwt = Crypt_1.Crypt.rootToken();
                }
                Logger_1.Logger.instanse.debug("DBHelper", "FindRoleByName", "Add role to cache : " + name);
                return Config_1.Config.db.GetOne({ query: { name: name, "_type": "role" }, collectionname: "users", jwt }, parent);
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(item))
                return null;
            return openflow_api_1.Role.assign(item);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async Save(item, jwt, parent) {
        await Config_1.Config.db._UpdateOne(null, item, "users", 2, false, jwt, parent);
    }
    async EnsureRole(jwt, name, id, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.EnsureRole", parent);
        try {
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureRole", `FindRoleByName ${name}`);
            let role = await this.FindRoleByName(name, jwt, span);
            if (role == null) {
                Logger_1.Logger.instanse.verbose("DBHelper", "EnsureRole", `EnsureRole FindRoleById ${name}`);
                role = await this.FindRoleById(id, null, span);
            }
            if (role !== null && (role._id === id || openflow_api_1.NoderedUtil.IsNullEmpty(id))) {
                return role;
            }
            if (role !== null && !openflow_api_1.NoderedUtil.IsNullEmpty(role._id)) {
                Logger_1.Logger.instanse.warn("DBHelper", "EnsureRole", `Deleting ${name} with ${role._id} not matcing expected id ${id}`);
                await Config_1.Config.db.DeleteOne(role._id, "users", false, jwt, span);
            }
            role = new openflow_api_1.Role();
            role.name = name;
            role._id = id;
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureRole", `Adding new role ${name}`);
            role = await Config_1.Config.db.InsertOne(role, "users", 0, false, jwt, span);
            role = openflow_api_1.Role.assign(role);
            openflow_api_1.Base.addRight(role, openflow_api_1.WellknownIds.admins, "admins", [openflow_api_1.Rights.full_control]);
            openflow_api_1.Base.addRight(role, role._id, role.name, [openflow_api_1.Rights.full_control]);
            openflow_api_1.Base.removeRight(role, role._id, [openflow_api_1.Rights.delete]);
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureRole", `Updating ACL for new role ${name}`);
            await this.Save(role, jwt, span);
            return openflow_api_1.Role.assign(role);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async EnsureUser(jwt, name, username, id, password, extraoptions, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.ensureUser", parent);
        try {
            span === null || span === void 0 ? void 0 : span.addEvent("FindByUsernameOrId");
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureUser", `FindById ${name} ${id}`);
            let user = await this.FindById(id, null, span);
            if (user == null) {
                Logger_1.Logger.instanse.verbose("DBHelper", "EnsureUser", `FindByUsername ${username}`);
                user = await this.FindByUsername(username, null, span);
            }
            if (user !== null && (user._id === id || id === null)) {
                return user;
            }
            if (user !== null && id !== null) {
                span === null || span === void 0 ? void 0 : span.addEvent("Deleting");
                Logger_1.Logger.instanse.warn("DBHelper", "EnsureUser", `Deleting ${name} with ${user._id} not matcing expected id ${id}`);
                await Config_1.Config.db.DeleteOne(user._id, "users", false, jwt, span);
            }
            user = new openflow_api_1.User();
            if (!openflow_api_1.NoderedUtil.IsNullUndefinded(extraoptions))
                user = Object.assign(user, extraoptions);
            user._id = id;
            user.name = name;
            user.username = username;
            if (password !== null && password !== undefined && password !== "") {
                span === null || span === void 0 ? void 0 : span.addEvent("SetPassword");
                await Crypt_1.Crypt.SetPassword(user, password, span);
            }
            else {
                span === null || span === void 0 ? void 0 : span.addEvent("SetPassword");
                await Crypt_1.Crypt.SetPassword(user, Math.random().toString(36).substr(2, 9), span);
            }
            span === null || span === void 0 ? void 0 : span.addEvent("Insert user");
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureUser", `Adding new user ${name}`);
            user = await Config_1.Config.db.InsertOne(user, "users", 0, false, jwt, span);
            user = openflow_api_1.User.assign(user);
            span === null || span === void 0 ? void 0 : span.addEvent("DecorateWithRoles");
            Logger_1.Logger.instanse.verbose("DBHelper", "EnsureRole", `Decorating new user ${name} with roles`);
            user = await this.DecorateWithRoles(user, span);
            span === null || span === void 0 ? void 0 : span.addEvent("return user");
            return user;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async EnsureNoderedRoles(user, jwt, force, parent) {
        if (Config_1.Config.auto_create_personal_nodered_group || force) {
            let name = user.username;
            // name = name.split("@").join("").split(".").join("");
            // name = name.toLowerCase();
            name = name.toLowerCase();
            name = name.replace(/([^a-z0-9]+){1,63}/gi, "");
            let noderedadmins = await this.FindRoleByName(name + "noderedadmins", jwt, parent);
            if (noderedadmins == null) {
                noderedadmins = await this.EnsureRole(jwt, name + "noderedadmins", null, parent);
                openflow_api_1.Base.addRight(noderedadmins, user._id, user.username, [openflow_api_1.Rights.full_control]);
                openflow_api_1.Base.removeRight(noderedadmins, user._id, [openflow_api_1.Rights.delete]);
                noderedadmins.AddMember(user);
                await this.Save(noderedadmins, jwt, parent);
            }
        }
        if (Config_1.Config.auto_create_personal_noderedapi_group || force) {
            let name = user.username;
            // name = name.split("@").join("").split(".").join("");
            // name = name.toLowerCase();
            name = name.toLowerCase();
            name = name.replace(/([^a-z0-9]+){1,63}/gi, "");
            let noderedadmins = await this.FindRoleByName(name + "nodered api users", jwt, parent);
            if (noderedadmins == null) {
                noderedadmins = await this.EnsureRole(jwt, name + "nodered api users", null, parent);
                openflow_api_1.Base.addRight(noderedadmins, user._id, user.username, [openflow_api_1.Rights.full_control]);
                openflow_api_1.Base.removeRight(noderedadmins, user._id, [openflow_api_1.Rights.delete]);
                noderedadmins.AddMember(user);
                await this.Save(noderedadmins, jwt, parent);
            }
        }
    }
    async UpdateHeartbeat(cli) {
        const dt = new Date(new Date().toISOString());
        const updatedoc = { _heartbeat: dt, lastseen: dt, clientagent: cli.clientagent, clientversion: cli.clientagent, remoteip: cli.remoteip };
        cli.user._heartbeat = dt;
        cli.user.lastseen = dt;
        if (cli.clientagent == "openrpa") {
            cli.user._rpaheartbeat = dt;
            return { $set: { ...updatedoc, _rpaheartbeat: new Date(new Date().toISOString()), _lastopenrpaclientversion: cli.clientversion } };
        }
        if (cli.clientagent == "nodered") {
            cli.user._noderedheartbeat = dt;
            return { $set: { ...updatedoc, _noderedheartbeat: new Date(new Date().toISOString()), _lastnoderedclientversion: cli.clientversion } };
        }
        if (cli.clientagent == "webapp" || cli.clientagent == "aiotwebapp") {
            cli.user._webheartbeat = dt;
            return { $set: { ...updatedoc, _webheartbeat: new Date(new Date().toISOString()), _lastwebappclientversion: cli.clientversion } };
        }
        if (cli.clientagent == "powershell") {
            cli.user._powershellheartbeat = dt;
            return { $set: { ...updatedoc, _powershellheartbeat: new Date(new Date().toISOString()), _lastpowershellclientversion: cli.clientversion } };
        }
        if (cli.clientagent == "mobileapp" || cli.clientagent == "aiotmobileapp") {
            cli.user._webheartbeat = dt;
            cli.user._mobilheartbeat = dt;
            return {
                $set: {
                    ...updatedoc, _webheartbeat: new Date(new Date().toISOString()), _lastwebappclientversion: cli.clientversion,
                    _mobilheartbeat: new Date(new Date().toISOString()), _lastmobilclientversion: cli.clientversion
                }
            };
        }
        else {
            return { $set: updatedoc };
        }
    }
    async GetIPBlockList(parent) {
        await this.init();
        const span = Logger_1.Logger.otel.startSubSpan("dbhelper.GetIPBlockList", parent);
        try {
            let items = await this.memoryCache.wrap("ipblock", () => {
                return Config_1.Config.db.query({ query: { _type: "ipblock" }, projection: { "ips": 1 }, top: 10, collectionname: "config", jwt: Crypt_1.Crypt.rootToken() }, span);
                ;
            });
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(items))
                items = [];
            return items;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    async ClearIPBlockList() {
        await this.memoryCache.del("ipblock");
    }
}
exports.DBHelper = DBHelper;
//# sourceMappingURL=DBHelper.js.map