"use strict";
(() => {
var exports = {};
exports.id = 6291;
exports.ids = [6291];
exports.modules = {

/***/ 439491:
/***/ ((module) => {

module.exports = require("assert");

/***/ }),

/***/ 714300:
/***/ ((module) => {

module.exports = require("buffer");

/***/ }),

/***/ 706113:
/***/ ((module) => {

module.exports = require("crypto");

/***/ }),

/***/ 809523:
/***/ ((module) => {

module.exports = require("dns");

/***/ }),

/***/ 582361:
/***/ ((module) => {

module.exports = require("events");

/***/ }),

/***/ 657147:
/***/ ((module) => {

module.exports = require("fs");

/***/ }),

/***/ 241808:
/***/ ((module) => {

module.exports = require("net");

/***/ }),

/***/ 822037:
/***/ ((module) => {

module.exports = require("os");

/***/ }),

/***/ 371017:
/***/ ((module) => {

module.exports = require("path");

/***/ }),

/***/ 677282:
/***/ ((module) => {

module.exports = require("process");

/***/ }),

/***/ 12781:
/***/ ((module) => {

module.exports = require("stream");

/***/ }),

/***/ 371576:
/***/ ((module) => {

module.exports = require("string_decoder");

/***/ }),

/***/ 924404:
/***/ ((module) => {

module.exports = require("tls");

/***/ }),

/***/ 76224:
/***/ ((module) => {

module.exports = require("tty");

/***/ }),

/***/ 257310:
/***/ ((module) => {

module.exports = require("url");

/***/ }),

/***/ 473837:
/***/ ((module) => {

module.exports = require("util");

/***/ }),

/***/ 26438:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (/* binding */ getParams)
/* harmony export */ });
function getParams(req) {
    if (req?.method == "POST") {
        return new Proxy({}, {
            get (_, key) {
                return req.body?.[key];
            }
        });
    /*
    // Disabled, since this could lead to a sneaky click on a link attack.
    // Should only be enabled for dev purposes.
    } else if (req?.method == "GET") {
    return new Proxy(
      {},
      {
        get(_, key) {
          if (typeof key != "string") {
            return undefined;
          }
          return req.query?.[key];
        },
      }
    );
*/ } else {
        // only support params for POST requests.
        return {};
    }
}


/***/ }),

/***/ 333584:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (/* binding */ handle)
/* harmony export */ });
/* harmony import */ var _cocalc_server_accounts_search__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(766266);
/* harmony import */ var _cocalc_server_accounts_search__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_cocalc_server_accounts_search__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var lib_api_get_params__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26438);
/*
Search for accounts matching a given query.

If user is signed in, then their account_id is used to prioritize the search.
*/ 

async function handle(req, res) {
    try {
        return res.json(await doUserSearch(req));
    } catch (err) {
        res.json({
            error: err.message
        });
    }
}
async function doUserSearch(req) {
    const { query  } = (0,lib_api_get_params__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .Z)(req);
    return await _cocalc_server_accounts_search__WEBPACK_IMPORTED_MODULE_0___default()({
        query
    });
}


/***/ }),

/***/ 766266:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {


/*
Search for users.

- by exact account_id
- by exact email_address
- by partial match on first_name and last_name
- by @username
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const pool_1 = __importDefault(__webpack_require__(541808));
const misc_1 = __webpack_require__(854006);
const util_1 = __webpack_require__(863563);
const logger_1 = __webpack_require__(227554);
const logger = (0, logger_1.getLogger)("accounts/search");
async function search({ 
/* account_id,*/
query, limit, admin, }) {
    limit = limit ?? 20;
    admin = !!admin;
    logger.debug("search for ", query);
    // One special case: when the query is just an email address or uuid.
    // We just return that account or empty list if no match.
    if ((0, misc_1.isValidUUID)(query)) {
        logger.debug("get user by account_id");
        const user = process(await getUserByAccountId(query), admin, false);
        return user ? [user] : [];
    }
    if ((0, misc_1.is_valid_email_address)(query)) {
        logger.debug("get user by email address");
        const user = process(await getUserByEmailAddress(query), admin, true);
        return user ? [user] : [];
    }
    const { string_queries, email_queries } = (0, misc_1.parse_user_search)(query);
    if (admin) {
        // For admin we just do substring queries anyways.
        for (const email_address of email_queries) {
            string_queries.push([email_address]);
        }
        email_queries.splice(0, email_queries.length); // empty array
    }
    const results = [];
    let matches = await getUsersByEmailAddresses(email_queries, limit);
    for (const user of matches) {
        const x = process(user, admin, true);
        if (x) {
            results.push(x);
        }
    }
    matches = await getUsersByStringQueries(string_queries, admin, limit - matches.length);
    for (const user of matches) {
        const x = process(user, admin, false);
        if (x) {
            results.push(x);
        }
    }
    results.sort((a, b) => -(0, misc_1.cmp)(Math.max(a.last_active ?? 0, a.created ?? 0), Math.max(b.last_active ?? 0, b.created ?? 0)));
    return results;
}
exports["default"] = search;
function process(user, admin = false, isEmailSearch) {
    if (user == null)
        return undefined;
    const x = { ...user };
    if (x.email_address && x.email_address_verified) {
        x.email_address_verified =
            x.email_address_verified[x.email_address] != null;
    }
    if (!admin) {
        if (!isEmailSearch) {
            delete x.email_address;
        }
        delete x.banned;
    }
    (0, util_1.toEpoch)(x, ["last_active", "created"]);
    return x;
}
const FIELDS = " account_id, first_name, last_name, name, email_address, last_active, created, banned, email_address_verified ";
async function getUserByEmailAddress(email_address) {
    const pool = (0, pool_1.default)("medium");
    const { rows } = await pool.query(`SELECT ${FIELDS} FROM accounts WHERE email_address=$1`, [email_address.toLowerCase()]);
    return rows[0];
}
async function getUserByAccountId(account_id) {
    const pool = (0, pool_1.default)("medium");
    const { rows } = await pool.query(`SELECT ${FIELDS} FROM accounts WHERE account_id=$1`, [account_id.toLowerCase()]);
    return rows[0];
}
async function getUsersByEmailAddresses(email_queries, limit) {
    logger.debug("getUsersByEmailAddresses", email_queries);
    if (email_queries.length == 0 || limit <= 0)
        return [];
    const pool = (0, pool_1.default)("medium");
    const { rows } = await pool.query(`SELECT ${FIELDS} FROM accounts WHERE email_address = ANY($1::TEXT[]) AND deleted IS NULL`, [email_queries]);
    return rows;
}
async function getUsersByStringQueries(string_queries, admin, limit) {
    logger.debug("getUsersByStringQueries", string_queries);
    if (limit <= 0 || string_queries.length <= 0) {
        return [];
    }
    /*  Substring search on first and last name, and for admin also email_address.
        With the two indexes, the query below is very fast, even on millions of accounts:
            CREATE INDEX accounts_first_name_idx ON accounts(first_name text_pattern_ops);
            CREATE INDEX accounts_last_name_idx  ON accounts(last_name text_pattern_ops);
    */
    const params = [];
    const where = [];
    let i = 1;
    for (const terms of string_queries) {
        const v = [];
        for (const s of terms) {
            v.push(`(lower(first_name) LIKE $${i}::TEXT OR lower(last_name) LIKE $${i}::TEXT OR '@' || lower(name) LIKE $${i}::TEXT ${admin ? `OR lower(email_address) LIKE $${i}::TEXT` : ""})`);
            params.push(`%${s}%`);
            i += 1;
        }
        where.push(`(${v.join(" AND ")})`);
    }
    let query = `SELECT ${FIELDS} FROM accounts WHERE deleted IS NOT TRUE AND (${where.join(" OR ")})`;
    if (!admin) {
        // Exclude unlisted users from search results
        query += " AND unlisted IS NOT true ";
    }
    // recently active users are much more relevant than old ones -- #2991
    query += " ORDER BY COALESCE(last_active, created) DESC NULLS LAST";
    query += ` LIMIT $${i}::INTEGER `;
    i += 1;
    params.push(limit);
    const pool = (0, pool_1.default)("medium");
    const { rows } = await pool.query(query, params);
    return rows;
}
//# sourceMappingURL=search.js.map

/***/ })

};
;

// load runtime
var __webpack_require__ = require("../../../../webpack-api-runtime.js");
__webpack_require__.C(exports);
var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
var __webpack_exports__ = __webpack_require__.X(0, [8180,2921,1808,4006,3563], () => (__webpack_exec__(333584)));
module.exports = __webpack_exports__;

})();