UNPKG

smc-hub

Version:

CoCalc: Backend webserver component

136 lines (126 loc) 4.32 kB
/* * This file is part of CoCalc: Copyright © 2020 Sagemath, Inc. * License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details */ import { PostgreSQL } from "../types"; import { is_valid_uuid_string, search_match, search_split, } from "smc-util/misc"; import { query } from "../query"; // This works and does the entire search in the database. // Unfortunately, it is unusably slow and I just don't have // the time to do something better right now: /* export async function matching_site_licenses( db: PostgreSQL, search: string, limit: number = 5 ): Promise<{ id: string }[]> { const where: any[] = []; const params: (string | number)[] = []; let i = 1; for (const s of search_split(search.toLowerCase())) { where.push( `(lower(site_licenses.title) LIKE $${i}::TEXT OR lower(site_licenses.description) LIKE $${i}::TEXT OR site_licenses.id::TEXT LIKE $${i}::TEXT OR lower(site_licenses.info::TEXT) LIKE $${i}::TEXT OR lower(accounts.first_name) LIKE $${i}::TEXT OR lower(accounts.last_name) LIKE $${i}::TEXT OR lower(accounts.email_address) LIKE $${i}::TEXT)` ); params.push(`%${s}%`); i += 1; } let query = "SELECT DISTINCT(site_licenses.id) AS id, site_licenses.last_used FROM site_licenses, accounts WHERE accounts.account_id::TEXT = ANY(site_licenses.managers) AND"; query += ` (${where.join(" AND ")})`; // recently active licenses are much more relevant than old ones query += " ORDER BY site_licenses.last_used DESC NULLS LAST"; query += ` LIMIT $${i}::INTEGER`; params.push(limit); i += 1; return (await db.async_query({ query, params })).rows; } */ // This is dumb but will be sufficiently fast up to probably 5K licenses. // This is not user facing functionality. We could maybe restrict to last_used // in the few months by default (optinally anything) and this would last // much longer... export async function matching_site_licenses( db: PostgreSQL, search: string, limit: number = 5 ): Promise<{ id: string }[]> { if (is_valid_uuid_string(search)) { return ( await db.async_query({ cache: true, query: "SELECT id FROM site_licenses WHERE id=$1", params: [search], }) ).rows; } // Get them all. const licenses = ( await db.async_query({ query: "SELECT id, id || ' ' || coalesce(lower(title),'') || ' ' || coalesce(lower(description),'') || ' ' || coalesce(lower(info::TEXT),'') AS info, managers FROM site_licenses ORDER BY last_used DESC NULLS LAST", }) ).rows; // Replace manager account ids by name and email const managers: Set<string> = new Set(); for (const x of licenses) { if (x.managers != null) { for (const account_id of x.managers) { managers.add(account_id); } } } const accounts: { [account_id: string]: string } = {}; for (const row of ( await db.async_query({ cache: true, query: "SELECT account_id, coalesce(lower(first_name),'') || ' ' || coalesce(lower(last_name),'') || ' ' || coalesce(lower(email_address),'') AS info FROM accounts WHERE account_id=ANY($1)", params: [Array.from(managers)], }) ).rows) { accounts[row.account_id] = row.info; } const v = search_split(search.toLowerCase()); const matches: { id: string }[] = []; for (const license of licenses) { let s = license.info; if (license.managers) { for (const account_id of license.managers) { s += " " + accounts[account_id]; } } if (search_match(s, v)) { matches.push({ id: license.id as string }); } if (matches.length >= limit) break; } return matches; } export async function manager_site_licenses( db: PostgreSQL, account_id: string ): Promise<object[]> { const query = "SELECT * FROM site_licenses WHERE $1=ANY(managers)"; const params = [account_id]; return (await db.async_query({ query, params })).rows; } // Return true if the given user is a manager of any site licenses. export async function is_a_site_license_manager( db: PostgreSQL, account_id: string ): Promise<boolean> { return ( ( await query({ db, query: "SELECT COUNT(*) FROM site_licenses WHERE $1=ANY(managers)", one: true, params: [account_id], }) ).count > 0 ); }