"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FormioEP = void 0;
const crypto = require("crypto");
const openflow_api_1 = require("@openiap/openflow-api");
const Logger_1 = require("../Logger");
const Crypt_1 = require("../Crypt");
const Config_1 = require("../Config");
const LoginProvider_1 = require("../LoginProvider");
const multer_gridfs_storage_1 = require("multer-gridfs-storage");
const mongodb_1 = require("mongodb");
const multer = require('multer');
const safeObjectID = (s) => mongodb_1.ObjectId.isValid(s) ? new mongodb_1.ObjectId(s) : null;
class FormioEP {
    static async configure(app, baseurl) {
        try {
            // Some times db is not connect yet, at this point, so wait til it is
            await Config_1.Config.db.connect();
            const storage = new multer_gridfs_storage_1.GridFsStorage({
                db: Config_1.Config.db.db,
                file: (req, file) => {
                    return new Promise((resolve, reject) => {
                        crypto.randomBytes(16, async (err, buf) => {
                            if (err) {
                                return reject(err);
                            }
                            // const filename = buf.toString('hex') + path.extname(file.originalname);
                            const filename = file.originalname;
                            const fileInfo = {
                                filename: filename,
                                metadata: new openflow_api_1.Base()
                            };
                            let user;
                            let jwt;
                            const authHeader = req.headers.authorization;
                            if (authHeader) {
                                user = await Crypt_1.Crypt.verityToken(authHeader);
                                jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                            }
                            else if (req.user) {
                                user = openflow_api_1.TokenUser.From(req.user);
                                jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                            }
                            const { query, headers } = req;
                            if (user === undefined)
                                throw new Error("Access denied, unknown user");
                            fileInfo.metadata.name = filename;
                            fileInfo.metadata.filename = filename;
                            fileInfo.metadata.path = "";
                            fileInfo.metadata.uniquename = query.uniquename;
                            fileInfo.metadata.form = query.form;
                            fileInfo.metadata.project = query.project;
                            fileInfo.metadata.baseurl = query.baseUrl;
                            fileInfo.metadata._acl = [];
                            fileInfo.metadata._createdby = user.name;
                            fileInfo.metadata._createdbyid = user._id;
                            fileInfo.metadata._created = new Date(new Date().toISOString());
                            fileInfo.metadata._modifiedby = user.name;
                            fileInfo.metadata._modifiedbyid = user._id;
                            fileInfo.metadata._modified = fileInfo.metadata._created;
                            const keys = Object.keys(query);
                            for (let i = 0; i < keys.length; i++) {
                                fileInfo.metadata[keys[i]] = query[keys[i]];
                            }
                            openflow_api_1.Base.addRight(fileInfo.metadata, user._id, user.name, [openflow_api_1.Rights.full_control]);
                            openflow_api_1.Base.addRight(fileInfo.metadata, openflow_api_1.WellknownIds.filestore_admins, "filestore admins", [openflow_api_1.Rights.full_control]);
                            openflow_api_1.Base.addRight(fileInfo.metadata, openflow_api_1.WellknownIds.filestore_users, "filestore users", [openflow_api_1.Rights.read]);
                            // Fix acl
                            fileInfo.metadata._acl.forEach((a, index) => {
                                if (typeof a.rights === "string") {
                                    fileInfo.metadata._acl[index].rights = new mongodb_1.Binary(Buffer.from(a.rights, "base64"), 0);
                                }
                            });
                            resolve(fileInfo);
                        });
                    });
                },
            });
            const upload = multer({
                storage: storage,
                limits: {
                    fileSize: (1000000 * Config_1.Config.upload_max_filesize_mb) // 25MB
                }
            }).any();
            app.delete("/upload", async (req, res, next) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.upload", req);
                try {
                    span === null || span === void 0 ? void 0 : span.setAttribute("remoteip", LoginProvider_1.LoginProvider.remoteip(req));
                    let user = null;
                    let jwt = null;
                    const authHeader = req.headers.authorization;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    const _query = req.query;
                    let uniquename = _query.uniquename;
                    let query = {};
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(uniquename)) {
                        if (Array.isArray(uniquename))
                            uniquename = uniquename.join("_");
                        if (uniquename.indexOf('/') > -1)
                            uniquename = uniquename.substr(0, uniquename.indexOf('/'));
                        query = { "metadata.uniquename": uniquename };
                    }
                    const arr = await Config_1.Config.db.query({ query, top: 1, orderby: { "uploadDate": -1 }, collectionname: "files", jwt }, span);
                    if (arr.length > 0) {
                        await Config_1.Config.db.DeleteOne(arr[0]._id, "files", false, jwt, span);
                    }
                    res.send({
                        status: "success",
                        display_status: "Success",
                        message: uniquename + " deleted"
                    });
                }
                catch (error) {
                    span === null || span === void 0 ? void 0 : span.recordException(error);
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
            app.get("/upload", async (req, res, next) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.upload", req);
                try {
                    span === null || span === void 0 ? void 0 : span.setAttribute("remoteip", LoginProvider_1.LoginProvider.remoteip(req));
                    let user = null;
                    let jwt = null;
                    const authHeader = req.headers.authorization;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    const _query = req.query;
                    let uniquename = _query.uniquename;
                    let _id = _query.id || _query._id;
                    let query = {};
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(uniquename)) {
                        if (Array.isArray(uniquename))
                            uniquename = uniquename.join("_");
                        if (uniquename.indexOf('/') > -1)
                            uniquename = uniquename.substr(0, uniquename.indexOf('/'));
                        query = { "metadata.uniquename": uniquename };
                    }
                    else if (!openflow_api_1.NoderedUtil.IsNullEmpty(_id)) {
                        query = { _id };
                    }
                    else {
                        return res.status(404).send({ message: 'nothing unique. Not found.' });
                    }
                    const arr = await Config_1.Config.db.query({ query, top: 1, orderby: { "uploadDate": -1 }, collectionname: "files", jwt }, span);
                    if (arr.length == 0) {
                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(uniquename)) {
                            return res.status(404).send({ message: 'uniquename ' + uniquename + ' Not found.' });
                        }
                        return res.status(404).send({ message: 'id ' + _id + ' Not found.' });
                    }
                    const id = arr[0]._id;
                    const rows = await Config_1.Config.db.query({ query: { _id: safeObjectID(id) }, top: 1, collectionname: "files", jwt }, span);
                    if (rows == null || rows.length != 1) {
                        return res.status(404).send({ message: 'id ' + id + ' Not found.' });
                    }
                    const file = rows[0];
                    const bucket = new mongodb_1.GridFSBucket(Config_1.Config.db.db);
                    let downloadStream = bucket.openDownloadStream(safeObjectID(id));
                    res.set('Content-Type', file.contentType);
                    res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
                    res.set('Content-Length', file.length);
                    downloadStream.on("error", function (err) {
                        res.end();
                    });
                    downloadStream.pipe(res);
                    return;
                }
                catch (error) {
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
            app.post("/upload", async (req, res) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.upload", req);
                try {
                    let user = null;
                    let jwt = null;
                    const authHeader = req.headers.authorization;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    upload(req, res, function (err) {
                        if (err) {
                            res.json({ error_code: 1, err_desc: err });
                            return;
                        }
                        LoginProvider_1.LoginProvider.redirect(res, req.headers.referer);
                        // res.json({ error_code: 0, err_desc: null });
                    });
                }
                catch (error) {
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
            app.get("/form", async (req, res, next) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.form", req);
                try {
                    const authHeader = req.headers.authorization;
                    let user = null;
                    let jwt = null;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    try {
                        // https://pc.openiap.io/form?type=resource&limit=4294967295&select=_id,title&limit=100&skip=0
                        if (req.query.type == "resource") {
                            var results = await Config_1.Config.db.query({ query: { _type: "resource" }, collectionname: "forms", jwt }, span);
                            results.forEach(result => {
                                // @ts-ignore
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(result.title))
                                    result.title = result.name;
                            });
                            return res.send(results);
                        }
                    }
                    catch (error) {
                        Logger_1.Logger.instanse.error(error, span);
                    }
                    res.status(404).send({ message: "unknown url" });
                }
                catch (error) {
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
            app.get("/form/:resource", async (req, res, next) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.form", req);
                try {
                    const authHeader = req.headers.authorization;
                    let user = null;
                    let jwt = null;
                    const resourceid = req.params.resource;
                    const limit = req.query.limit;
                    const skip = req.query.skip;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    if (openflow_api_1.NoderedUtil.IsNullEmpty(resourceid)) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    // https://pc.openiap.io/form/1/submission?limit=100&skip=0
                    // https://pc.openiap.io/form/1?limit=100&skip=0
                    const resource = await Config_1.Config.db.getbyid(resourceid, "forms", jwt, true, null);
                    // @ts-ignore
                    if (openflow_api_1.NoderedUtil.IsNullEmpty(resource.label))
                        resource.label = resource.name;
                    this.expandobject(resource.aggregates);
                    resource.aggregates.push({ "$limit": 1 });
                    var dbresults = await Config_1.Config.db.aggregate(resource.aggregates, resource.collection, jwt, null, null);
                    if (dbresults.length > 0) {
                        const keys = Object.keys(dbresults[0]);
                        let components = [];
                        keys.forEach(key => {
                            components.push({ "label": key, "key": key, "input": true, "inputType": "email", "hidden": false });
                        });
                        resource.components = components;
                    }
                    return res.send(resource);
                }
                catch (error) {
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
            app.get("/form/:resourceid/submission", async (req, res, next) => {
                const span = Logger_1.Logger.otel.startSpanExpress("LoginProvider.form", req);
                try {
                    const authHeader = req.headers.authorization;
                    let user = null;
                    let jwt = null;
                    const resourceid = req.params.resourceid;
                    const query = req.query.query || req.query.q;
                    const limit = req.query.limit;
                    const skip = req.query.skip;
                    const sort = req.query.sort;
                    if (authHeader) {
                        user = await Crypt_1.Crypt.verityToken(authHeader);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    else if (req.user) {
                        user = openflow_api_1.TokenUser.From(req.user);
                        jwt = Crypt_1.Crypt.createToken(user, Config_1.Config.downloadtoken_expires_in);
                    }
                    if (user == null) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    if (openflow_api_1.NoderedUtil.IsNullEmpty(resourceid)) {
                        return res.status(404).send({ message: 'Route ' + req.url + ' Not found.' });
                    }
                    let resource = await Config_1.Config.db.getbyid(resourceid, "forms", jwt, true, span);
                    if (openflow_api_1.NoderedUtil.IsNullUndefinded(resource)) {
                        return res.status(404).send({ message: 'Route ' + resourceid + ' Not found.' });
                    }
                    this.expandobject(resource.aggregates);
                    var keys = Object.keys(req.query);
                    var queryfields = keys.filter(x => x.startsWith("data.") && x.endsWith("__regex"));
                    if (queryfields.length > 0) {
                        let ors = [];
                        let logfields = [];
                        queryfields.forEach(f => {
                            let field = f.substring(5, f.length - 7);
                            logfields.push(field);
                            let value = req.query[f];
                            let m = {};
                            m[field] = { $regex: ".*" + value + ".*", $options: "si" };
                            ors.push(m);
                        });
                        resource.aggregates.unshift({ "$match": { "$or": ors } });
                        Logger_1.Logger.instanse.debug("searching using " + logfields.join(", ") + " fields", span);
                    }
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(query)) {
                        resource.aggregates.unshift({ "$match": { name: { $regex: ".*" + query + ".*", $options: "si" } } });
                    }
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(sort)) {
                        var sorts = sort.split(",");
                        var orderby = {};
                        sorts.forEach(s => {
                            orderby[s] = 1;
                        });
                        resource.aggregates.push({ "$sort": orderby });
                        Logger_1.Logger.instanse.debug("sort using " + sorts.join(", ") + " fields", span);
                    }
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(skip) && !isNaN(skip) && skip > 0) {
                        resource.aggregates.push({ "$skip": +skip });
                    }
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(limit) && !isNaN(limit) && limit > 0) {
                        resource.aggregates.push({ "$limit": +limit });
                    }
                    var dbresults = await Config_1.Config.db.aggregate(resource.aggregates, resource.collection, jwt, null, span);
                    var results = [];
                    dbresults.forEach(result => {
                        // @ts-ignore
                        // if (NoderedUtil.IsNullEmpty(result.label)) result.label = result.name;
                        // results.push({ _id: result._id, data: result })
                        // results.push({ _id: result._id, data: result })
                        results.push({ "data": result, label: result.name, id: result._id });
                    });
                    Logger_1.Logger.instanse.info("Return " + dbresults.length + " items based of resource " + resource.name, span);
                    return res.send(results);
                }
                catch (error) {
                    Logger_1.Logger.instanse.error(error, span);
                    return res.status(500).send({ message: error.message ? error.message : error });
                }
                finally {
                    Logger_1.Logger.otel.endSpan(span);
                }
            });
        }
        catch (error) {
            Logger_1.Logger.instanse.error(error, null);
        }
    }
    static expandobject(o) {
        const keys = Object.keys(o);
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i];
            if (key.startsWith("___")) {
                let newkey = "$" + key.substr(3);
                o[newkey] = o[key];
                delete o[key];
                key = newkey;
            }
            if (typeof (o[key]) === "object") {
                this.expandobject(o[key]);
            }
        }
    }
}
exports.FormioEP = FormioEP;
//# sourceMappingURL=FormioEP.js.map