2022-04-28 23:39:21 +02:00
|
|
|
import { newid } from "../hashing"
|
|
|
|
import { DEFAULT_TENANT_ID, Configs } from "../constants"
|
2022-05-03 23:58:19 +02:00
|
|
|
import env from "../environment"
|
2022-07-04 12:12:08 +02:00
|
|
|
import { SEPARATOR, DocumentTypes, UNICODE_MAX, ViewNames } from "./constants"
|
2022-05-23 10:04:00 +02:00
|
|
|
import { getTenantId, getGlobalDBName, getGlobalDB } from "../tenancy"
|
2022-04-28 23:39:21 +02:00
|
|
|
import fetch from "node-fetch"
|
|
|
|
import { doWithDB, allDbs } from "./index"
|
2022-05-05 09:47:06 +02:00
|
|
|
import { getCouchInfo } from "./pouch"
|
2022-04-28 23:39:21 +02:00
|
|
|
import { getAppMetadata } from "../cache/appMetadata"
|
|
|
|
import { checkSlashesInUrl } from "../helpers"
|
|
|
|
import { isDevApp, isDevAppID } from "./conversions"
|
2022-05-23 10:04:00 +02:00
|
|
|
import { APP_PREFIX } from "./constants"
|
2022-05-23 23:14:44 +02:00
|
|
|
import * as events from "../events"
|
2021-11-16 11:30:37 +01:00
|
|
|
|
2022-04-28 23:39:21 +02:00
|
|
|
export * from "./constants"
|
|
|
|
export * from "./conversions"
|
|
|
|
export { default as Replication } from "./Replication"
|
2021-04-13 14:26:13 +02:00
|
|
|
|
2022-05-09 13:29:44 +02:00
|
|
|
/**
|
|
|
|
* Generates a new app ID.
|
|
|
|
* @returns {string} The new app ID which the app doc can be stored under.
|
|
|
|
*/
|
2022-05-23 10:04:00 +02:00
|
|
|
export const generateAppID = (tenantId = null) => {
|
2022-05-09 13:29:44 +02:00
|
|
|
let id = APP_PREFIX
|
|
|
|
if (tenantId) {
|
|
|
|
id += `${tenantId}${SEPARATOR}`
|
|
|
|
}
|
|
|
|
return `${id}${newid()}`
|
|
|
|
}
|
|
|
|
|
2021-05-14 16:43:41 +02:00
|
|
|
/**
|
|
|
|
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
|
|
|
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
|
|
|
* More complex cases such as link docs and rows which have multiple levels of IDs that their
|
|
|
|
* ID consists of need their own functions to build the allDocs parameters.
|
|
|
|
* @param {string} docType The type of document which input params are being built for, e.g. user,
|
|
|
|
* link, app, table and so on.
|
|
|
|
* @param {string|null} docId The ID of the document minus its type - this is only needed if looking
|
|
|
|
* for a singular document.
|
|
|
|
* @param {object} otherProps Add any other properties onto the request, e.g. include_docs.
|
|
|
|
* @returns {object} Parameters which can then be used with an allDocs request.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function getDocParams(
|
|
|
|
docType: any,
|
|
|
|
docId: any = null,
|
|
|
|
otherProps: any = {}
|
|
|
|
) {
|
2021-05-14 16:43:41 +02:00
|
|
|
if (docId == null) {
|
|
|
|
docId = ""
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...otherProps,
|
|
|
|
startkey: `${docType}${SEPARATOR}${docId}`,
|
|
|
|
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-27 19:00:48 +02:00
|
|
|
/**
|
|
|
|
* Retrieve the correct index for a view based on default design DB.
|
|
|
|
*/
|
2022-07-04 12:12:08 +02:00
|
|
|
export function getQueryIndex(viewName: ViewNames) {
|
2022-06-27 19:00:48 +02:00
|
|
|
return `database/${viewName}`
|
|
|
|
}
|
2021-05-14 16:43:41 +02:00
|
|
|
|
2021-04-19 12:34:07 +02:00
|
|
|
/**
|
2021-08-05 10:59:08 +02:00
|
|
|
* Generates a new workspace ID.
|
|
|
|
* @returns {string} The new workspace ID which the workspace doc can be stored under.
|
2021-04-19 12:34:07 +02:00
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function generateWorkspaceID() {
|
2021-08-05 10:59:08 +02:00
|
|
|
return `${DocumentTypes.WORKSPACE}${SEPARATOR}${newid()}`
|
2021-04-19 12:34:07 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 12:33:16 +02:00
|
|
|
/**
|
2021-08-05 10:59:08 +02:00
|
|
|
* Gets parameters for retrieving workspaces.
|
2021-04-19 17:16:46 +02:00
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function getWorkspaceParams(id = "", otherProps = {}) {
|
2021-04-19 17:16:46 +02:00
|
|
|
return {
|
|
|
|
...otherProps,
|
2021-08-05 10:59:08 +02:00
|
|
|
startkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}`,
|
|
|
|
endkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`,
|
2021-04-19 17:16:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-04-20 18:17:44 +02:00
|
|
|
* Generates a new global user ID.
|
|
|
|
* @returns {string} The new user ID which the user doc can be stored under.
|
|
|
|
*/
|
2022-06-13 23:26:15 +02:00
|
|
|
export function generateGlobalUserID(id?: any) {
|
2021-04-21 22:08:04 +02:00
|
|
|
return `${DocumentTypes.USER}${SEPARATOR}${id || newid()}`
|
2021-04-20 18:17:44 +02:00
|
|
|
}
|
|
|
|
|
2021-04-19 17:16:46 +02:00
|
|
|
/**
|
|
|
|
* Gets parameters for retrieving users.
|
2021-04-07 12:33:16 +02:00
|
|
|
*/
|
2022-06-29 20:11:00 +02:00
|
|
|
export function getGlobalUserParams(globalId: any, otherProps: any = {}) {
|
2021-04-19 18:31:47 +02:00
|
|
|
if (!globalId) {
|
|
|
|
globalId = ""
|
2021-04-09 16:11:49 +02:00
|
|
|
}
|
2022-06-29 20:11:00 +02:00
|
|
|
const startkey = otherProps?.startkey
|
2021-04-07 12:33:16 +02:00
|
|
|
return {
|
|
|
|
...otherProps,
|
2022-06-29 20:11:00 +02:00
|
|
|
// need to include this incase pagination
|
|
|
|
startkey: startkey
|
|
|
|
? startkey
|
|
|
|
: `${DocumentTypes.USER}${SEPARATOR}${globalId}`,
|
2021-04-19 18:31:47 +02:00
|
|
|
endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`,
|
2021-04-07 12:33:16 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-21 17:42:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a template ID.
|
2021-08-05 10:59:08 +02:00
|
|
|
* @param ownerId The owner/user of the template, this could be global or a workspace level.
|
2021-04-21 17:42:44 +02:00
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function generateTemplateID(ownerId: any) {
|
2021-05-10 15:41:52 +02:00
|
|
|
return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}`
|
2021-04-21 17:42:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-08-05 10:59:08 +02:00
|
|
|
* Gets parameters for retrieving templates. Owner ID must be specified, either global or a workspace level.
|
2021-04-21 17:42:44 +02:00
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function getTemplateParams(
|
|
|
|
ownerId: any,
|
|
|
|
templateId: any,
|
|
|
|
otherProps = {}
|
|
|
|
) {
|
2021-04-21 17:42:44 +02:00
|
|
|
if (!templateId) {
|
|
|
|
templateId = ""
|
|
|
|
}
|
2021-04-21 19:15:57 +02:00
|
|
|
let final
|
|
|
|
if (templateId) {
|
|
|
|
final = templateId
|
|
|
|
} else {
|
|
|
|
final = `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}`
|
2021-04-09 16:11:49 +02:00
|
|
|
}
|
2021-04-07 12:33:16 +02:00
|
|
|
return {
|
|
|
|
...otherProps,
|
2021-04-21 17:42:44 +02:00
|
|
|
startkey: final,
|
|
|
|
endkey: `${final}${UNICODE_MAX}`,
|
2021-04-07 12:33:16 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-20 19:14:36 +02:00
|
|
|
|
2022-06-15 17:51:30 +02:00
|
|
|
/**
|
|
|
|
* Generates a new user group ID
|
|
|
|
* @returns {string} The new user group ID which info can be stored under.
|
|
|
|
*/
|
|
|
|
exports.generateUserGroupID = () => {
|
|
|
|
return `${DocumentTypes.GROUP}${SEPARATOR}${newid()}`
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets parameters for retrieving groups.
|
|
|
|
*/
|
2022-07-06 09:45:22 +02:00
|
|
|
exports.getUserGroupsParams = (groupId: any, otherProps = {}) => {
|
2022-06-15 17:51:30 +02:00
|
|
|
if (!groupId) {
|
|
|
|
groupId = ""
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...otherProps,
|
|
|
|
startkey: `${DocumentTypes.GROUP}${SEPARATOR}${groupId}`,
|
|
|
|
endkey: `${DocumentTypes.GROUP}${SEPARATOR}${groupId}${UNICODE_MAX}`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 16:43:41 +02:00
|
|
|
/**
|
|
|
|
* Generates a new role ID.
|
|
|
|
* @returns {string} The new role ID which the role doc can be stored under.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function generateRoleID(id: any) {
|
2021-05-14 16:43:41 +02:00
|
|
|
return `${DocumentTypes.ROLE}${SEPARATOR}${id || newid()}`
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets parameters for retrieving a role, this is a utility function for the getDocParams function.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export function getRoleParams(roleId = null, otherProps = {}) {
|
2021-05-14 16:43:41 +02:00
|
|
|
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
|
|
|
|
}
|
|
|
|
|
2022-04-28 23:39:21 +02:00
|
|
|
export function getStartEndKeyURL(base: any, baseKey: any, tenantId = null) {
|
2021-12-10 16:17:18 +01:00
|
|
|
const tenancy = tenantId ? `${SEPARATOR}${tenantId}` : ""
|
|
|
|
return `${base}?startkey="${baseKey}${tenancy}"&endkey="${baseKey}${tenancy}${UNICODE_MAX}"`
|
2021-12-10 16:10:45 +01:00
|
|
|
}
|
|
|
|
|
2021-08-06 17:38:07 +02:00
|
|
|
/**
|
|
|
|
* if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing
|
|
|
|
* when using Pouch it will use the pouchdb-all-dbs package.
|
2022-02-16 12:59:43 +01:00
|
|
|
* opts.efficient can be provided to make sure this call is always quick in a multi-tenant environment,
|
|
|
|
* but it may not be 100% accurate in full efficiency mode (some tenantless apps may be missed).
|
2021-08-06 17:38:07 +02:00
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function getAllDbs(opts = { efficient: false }) {
|
2022-02-16 12:59:43 +01:00
|
|
|
const efficient = opts && opts.efficient
|
2021-08-06 17:38:07 +02:00
|
|
|
// specifically for testing we use the pouch package for this
|
|
|
|
if (env.isTest()) {
|
2022-03-29 17:03:44 +02:00
|
|
|
return allDbs()
|
2021-08-06 17:38:07 +02:00
|
|
|
}
|
2022-05-05 09:47:06 +02:00
|
|
|
let dbs: any[] = []
|
2022-04-27 17:56:30 +02:00
|
|
|
let { url, cookie } = getCouchInfo()
|
2022-05-05 09:47:06 +02:00
|
|
|
async function addDbs(couchUrl: string) {
|
2022-04-27 17:56:30 +02:00
|
|
|
const response = await fetch(checkSlashesInUrl(encodeURI(couchUrl)), {
|
|
|
|
method: "GET",
|
|
|
|
headers: {
|
|
|
|
Authorization: cookie,
|
|
|
|
},
|
|
|
|
})
|
2021-12-10 16:10:45 +01:00
|
|
|
if (response.status === 200) {
|
|
|
|
let json = await response.json()
|
|
|
|
dbs = dbs.concat(json)
|
|
|
|
} else {
|
|
|
|
throw "Cannot connect to CouchDB instance"
|
|
|
|
}
|
|
|
|
}
|
2022-04-27 17:56:30 +02:00
|
|
|
let couchUrl = `${url}/_all_dbs`
|
2022-01-11 13:31:40 +01:00
|
|
|
let tenantId = getTenantId()
|
2022-02-16 12:59:43 +01:00
|
|
|
if (!env.MULTI_TENANCY || (!efficient && tenantId === DEFAULT_TENANT_ID)) {
|
2022-01-11 13:31:40 +01:00
|
|
|
// just get all DBs when:
|
|
|
|
// - single tenancy
|
|
|
|
// - default tenant
|
|
|
|
// - apps dbs don't contain tenant id
|
|
|
|
// - non-default tenant dbs are filtered out application side in getAllApps
|
|
|
|
await addDbs(couchUrl)
|
|
|
|
} else {
|
2021-12-10 16:10:45 +01:00
|
|
|
// get prod apps
|
2022-04-28 23:39:21 +02:00
|
|
|
await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP, tenantId))
|
2021-12-10 16:10:45 +01:00
|
|
|
// get dev apps
|
2022-04-28 23:39:21 +02:00
|
|
|
await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP_DEV, tenantId))
|
2021-12-10 16:10:45 +01:00
|
|
|
// add global db name
|
|
|
|
dbs.push(getGlobalDBName(tenantId))
|
2021-08-06 17:38:07 +02:00
|
|
|
}
|
2021-12-10 16:10:45 +01:00
|
|
|
return dbs
|
2021-08-06 17:38:07 +02:00
|
|
|
}
|
|
|
|
|
2021-05-14 17:31:07 +02:00
|
|
|
/**
|
|
|
|
* Lots of different points in the system need to find the full list of apps, this will
|
|
|
|
* enumerate the entire CouchDB cluster and get the list of databases (every app).
|
2022-02-01 21:29:12 +01:00
|
|
|
*
|
2021-05-14 17:31:07 +02:00
|
|
|
* @return {Promise<object[]>} returns the app information document stored in each app database.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) {
|
2021-08-05 10:59:08 +02:00
|
|
|
let tenantId = getTenantId()
|
|
|
|
if (!env.MULTI_TENANCY && !tenantId) {
|
|
|
|
tenantId = DEFAULT_TENANT_ID
|
|
|
|
}
|
2022-04-28 23:39:21 +02:00
|
|
|
let dbs = await getAllDbs({ efficient })
|
|
|
|
const appDbNames = dbs.filter((dbName: any) => {
|
2021-08-05 10:59:08 +02:00
|
|
|
const split = dbName.split(SEPARATOR)
|
|
|
|
// it is an app, check the tenantId
|
|
|
|
if (split[0] === DocumentTypes.APP) {
|
|
|
|
// tenantId is always right before the UUID
|
|
|
|
const possibleTenantId = split[split.length - 2]
|
2021-09-14 12:16:28 +02:00
|
|
|
|
|
|
|
const noTenantId =
|
|
|
|
split.length === 2 || possibleTenantId === DocumentTypes.DEV
|
|
|
|
|
2021-08-05 10:59:08 +02:00
|
|
|
return (
|
|
|
|
(tenantId === DEFAULT_TENANT_ID && noTenantId) ||
|
|
|
|
possibleTenantId === tenantId
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
2021-08-06 18:14:31 +02:00
|
|
|
if (idsOnly) {
|
|
|
|
return appDbNames
|
|
|
|
}
|
2022-04-28 23:39:21 +02:00
|
|
|
const appPromises = appDbNames.map((app: any) =>
|
2021-06-08 19:06:16 +02:00
|
|
|
// skip setup otherwise databases could be re-created
|
2022-03-29 17:03:44 +02:00
|
|
|
getAppMetadata(app)
|
2021-05-17 22:43:50 +02:00
|
|
|
)
|
2021-05-14 17:31:07 +02:00
|
|
|
if (appPromises.length === 0) {
|
|
|
|
return []
|
|
|
|
} else {
|
|
|
|
const response = await Promise.allSettled(appPromises)
|
|
|
|
const apps = response
|
2022-04-28 23:39:21 +02:00
|
|
|
.filter(
|
|
|
|
(result: any) => result.status === "fulfilled" && result.value != null
|
|
|
|
)
|
|
|
|
.map(({ value }: any) => value)
|
2021-05-19 16:09:57 +02:00
|
|
|
if (!all) {
|
2022-04-28 23:39:21 +02:00
|
|
|
return apps.filter((app: any) => {
|
2021-05-19 16:09:57 +02:00
|
|
|
if (dev) {
|
|
|
|
return isDevApp(app)
|
|
|
|
}
|
|
|
|
return !isDevApp(app)
|
|
|
|
})
|
|
|
|
} else {
|
2022-04-28 23:39:21 +02:00
|
|
|
return apps.map((app: any) => ({
|
2021-05-19 16:09:57 +02:00
|
|
|
...app,
|
2021-05-19 16:55:00 +02:00
|
|
|
status: isDevApp(app) ? "development" : "published",
|
2021-05-19 16:09:57 +02:00
|
|
|
}))
|
|
|
|
}
|
2021-05-14 17:31:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-10 13:00:29 +01:00
|
|
|
/**
|
|
|
|
* Utility function for getAllApps but filters to production apps only.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function getProdAppIDs() {
|
|
|
|
return (await getAllApps({ idsOnly: true })).filter(
|
|
|
|
(id: any) => !isDevAppID(id)
|
2021-11-10 13:00:29 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility function for the inverse of above.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function getDevAppIDs() {
|
|
|
|
return (await getAllApps({ idsOnly: true })).filter((id: any) =>
|
|
|
|
isDevAppID(id)
|
2021-11-10 13:00:29 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function dbExists(dbName: any) {
|
2021-06-08 19:06:16 +02:00
|
|
|
let exists = false
|
2022-04-19 20:42:52 +02:00
|
|
|
return doWithDB(
|
|
|
|
dbName,
|
2022-04-28 23:39:21 +02:00
|
|
|
async (db: any) => {
|
2022-04-19 20:42:52 +02:00
|
|
|
try {
|
|
|
|
// check if database exists
|
|
|
|
const info = await db.info()
|
|
|
|
if (info && !info.error) {
|
|
|
|
exists = true
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
exists = false
|
|
|
|
}
|
|
|
|
return exists
|
|
|
|
},
|
|
|
|
{ skip_setup: true }
|
|
|
|
)
|
2021-06-08 19:06:16 +02:00
|
|
|
}
|
|
|
|
|
2021-04-20 19:14:36 +02:00
|
|
|
/**
|
|
|
|
* Generates a new configuration ID.
|
|
|
|
* @returns {string} The new configuration ID which the config doc can be stored under.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export const generateConfigID = ({ type, workspace, user }: any) => {
|
2021-08-05 10:59:08 +02:00
|
|
|
const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR)
|
2021-04-20 19:14:36 +02:00
|
|
|
|
2021-04-22 12:45:22 +02:00
|
|
|
return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`
|
2021-04-20 19:14:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets parameters for retrieving configurations.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export const getConfigParams = (
|
|
|
|
{ type, workspace, user }: any,
|
|
|
|
otherProps = {}
|
|
|
|
) => {
|
2021-08-05 10:59:08 +02:00
|
|
|
const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR)
|
2021-04-22 12:45:22 +02:00
|
|
|
|
2021-04-20 19:14:36 +02:00
|
|
|
return {
|
|
|
|
...otherProps,
|
2021-04-22 12:45:22 +02:00
|
|
|
startkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`,
|
|
|
|
endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`,
|
2021-04-20 19:14:36 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-22 14:46:54 +02:00
|
|
|
|
2022-02-10 20:06:49 +01:00
|
|
|
/**
|
|
|
|
* Generates a new dev info document ID - this is scoped to a user.
|
|
|
|
* @returns {string} The new dev info ID which info for dev (like api key) can be stored under.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export const generateDevInfoID = (userId: any) => {
|
2022-02-10 20:06:49 +01:00
|
|
|
return `${DocumentTypes.DEV_INFO}${SEPARATOR}${userId}`
|
|
|
|
}
|
|
|
|
|
2021-04-22 14:46:54 +02:00
|
|
|
/**
|
2021-08-05 10:59:08 +02:00
|
|
|
* Returns the most granular configuration document from the DB based on the type, workspace and userID passed.
|
2021-04-22 15:53:19 +02:00
|
|
|
* @param {Object} db - db instance to query
|
2021-08-05 10:59:08 +02:00
|
|
|
* @param {Object} scopes - the type, workspace and userID scopes of the configuration.
|
2021-04-22 14:46:54 +02:00
|
|
|
* @returns The most granular configuration document based on the scope.
|
|
|
|
*/
|
2022-04-28 23:39:21 +02:00
|
|
|
export const getScopedFullConfig = async function (
|
|
|
|
db: any,
|
|
|
|
{ type, user, workspace }: any
|
|
|
|
) {
|
2021-04-22 14:46:54 +02:00
|
|
|
const response = await db.allDocs(
|
|
|
|
getConfigParams(
|
2021-08-05 10:59:08 +02:00
|
|
|
{ type, user, workspace },
|
2021-04-22 14:46:54 +02:00
|
|
|
{
|
|
|
|
include_docs: true,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
2021-05-04 18:31:06 +02:00
|
|
|
|
2022-04-28 23:39:21 +02:00
|
|
|
function determineScore(row: any) {
|
2021-04-22 15:07:00 +02:00
|
|
|
const config = row.doc
|
2021-04-22 14:46:54 +02:00
|
|
|
|
2021-08-05 10:59:08 +02:00
|
|
|
// Config is specific to a user and a workspace
|
|
|
|
if (config._id.includes(generateConfigID({ type, user, workspace }))) {
|
2021-05-04 18:31:06 +02:00
|
|
|
return 4
|
2021-04-22 15:07:00 +02:00
|
|
|
} else if (config._id.includes(generateConfigID({ type, user }))) {
|
|
|
|
// Config is specific to a user only
|
2021-05-04 18:31:06 +02:00
|
|
|
return 3
|
2021-08-05 10:59:08 +02:00
|
|
|
} else if (config._id.includes(generateConfigID({ type, workspace }))) {
|
|
|
|
// Config is specific to a workspace only
|
2021-05-04 18:31:06 +02:00
|
|
|
return 2
|
2021-04-22 15:07:00 +02:00
|
|
|
} else if (config._id.includes(generateConfigID({ type }))) {
|
|
|
|
// Config is specific to a type only
|
2021-05-04 18:31:06 +02:00
|
|
|
return 1
|
2021-04-22 14:46:54 +02:00
|
|
|
}
|
2021-05-04 18:31:06 +02:00
|
|
|
return 0
|
|
|
|
}
|
2021-04-22 14:46:54 +02:00
|
|
|
|
2021-04-22 15:07:00 +02:00
|
|
|
// Find the config with the most granular scope based on context
|
2021-11-12 14:31:55 +01:00
|
|
|
let scopedConfig = response.rows.sort(
|
2022-04-28 23:39:21 +02:00
|
|
|
(a: any, b: any) => determineScore(a) - determineScore(b)
|
2021-05-04 19:14:13 +02:00
|
|
|
)[0]
|
2021-04-22 15:07:00 +02:00
|
|
|
|
2021-11-12 14:31:55 +01:00
|
|
|
// custom logic for settings doc
|
|
|
|
if (type === Configs.SETTINGS) {
|
|
|
|
if (scopedConfig && scopedConfig.doc) {
|
2022-05-23 23:14:44 +02:00
|
|
|
// overrides affected by environment variables
|
2022-07-04 13:54:26 +02:00
|
|
|
scopedConfig.doc.config.platformUrl = await getPlatformUrl({
|
|
|
|
tenantAware: true,
|
|
|
|
})
|
2022-05-23 23:14:44 +02:00
|
|
|
scopedConfig.doc.config.analyticsEnabled =
|
|
|
|
await events.analytics.enabled()
|
2021-11-12 14:31:55 +01:00
|
|
|
} else {
|
2022-05-23 23:14:44 +02:00
|
|
|
// defaults
|
2021-11-12 14:31:55 +01:00
|
|
|
scopedConfig = {
|
|
|
|
doc: {
|
2022-05-30 22:46:08 +02:00
|
|
|
_id: generateConfigID({ type, user, workspace }),
|
2021-11-12 14:31:55 +01:00
|
|
|
config: {
|
2022-07-04 13:54:26 +02:00
|
|
|
platformUrl: await getPlatformUrl({ tenantAware: true }),
|
2022-05-23 23:14:44 +02:00
|
|
|
analyticsEnabled: await events.analytics.enabled(),
|
2021-11-12 14:31:55 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-05 21:58:31 +02:00
|
|
|
return scopedConfig && scopedConfig.doc
|
2021-04-22 14:46:54 +02:00
|
|
|
}
|
|
|
|
|
2022-07-04 13:54:26 +02:00
|
|
|
export const getPlatformUrl = async (opts = { tenantAware: true }) => {
|
2021-12-10 16:10:45 +01:00
|
|
|
let platformUrl = env.PLATFORM_URL || "http://localhost:10000"
|
2021-11-12 14:31:55 +01:00
|
|
|
|
2022-05-17 10:52:38 +02:00
|
|
|
if (!env.SELF_HOSTED && env.MULTI_TENANCY && opts.tenantAware) {
|
2021-11-12 14:31:55 +01:00
|
|
|
// cloud and multi tenant - add the tenant to the default platform url
|
|
|
|
const tenantId = getTenantId()
|
|
|
|
if (!platformUrl.includes("localhost:")) {
|
|
|
|
platformUrl = platformUrl.replace("://", `://${tenantId}.`)
|
|
|
|
}
|
2022-05-17 10:52:38 +02:00
|
|
|
} else if (env.SELF_HOSTED) {
|
|
|
|
const db = getGlobalDB()
|
|
|
|
// get the doc directly instead of with getScopedConfig to prevent loop
|
|
|
|
let settings
|
|
|
|
try {
|
|
|
|
settings = await db.get(generateConfigID({ type: Configs.SETTINGS }))
|
2022-05-23 11:29:33 +02:00
|
|
|
} catch (e: any) {
|
2022-05-17 10:52:38 +02:00
|
|
|
if (e.status !== 404) {
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 14:31:55 +01:00
|
|
|
// self hosted - check for platform url override
|
2022-05-17 10:52:38 +02:00
|
|
|
if (settings && settings.config && settings.config.platformUrl) {
|
|
|
|
platformUrl = settings.config.platformUrl
|
2021-11-12 14:31:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 16:18:57 +01:00
|
|
|
return platformUrl
|
2021-11-12 14:31:55 +01:00
|
|
|
}
|
|
|
|
|
2022-06-29 20:11:00 +02:00
|
|
|
export function pagination(
|
2022-06-30 16:39:26 +02:00
|
|
|
data: any[],
|
2022-06-29 20:11:00 +02:00
|
|
|
pageSize: number,
|
2022-06-30 16:39:26 +02:00
|
|
|
{ paginate, property } = { paginate: true, property: "_id" }
|
2022-06-29 20:11:00 +02:00
|
|
|
) {
|
|
|
|
if (!paginate) {
|
|
|
|
return { data, hasNextPage: false }
|
|
|
|
}
|
|
|
|
const hasNextPage = data.length > pageSize
|
2022-06-30 16:39:26 +02:00
|
|
|
let nextPage = undefined
|
|
|
|
if (hasNextPage) {
|
|
|
|
nextPage = property ? data[pageSize]?.[property] : data[pageSize]?._id
|
|
|
|
}
|
2022-06-29 20:11:00 +02:00
|
|
|
return {
|
|
|
|
data: data.slice(0, pageSize),
|
|
|
|
hasNextPage,
|
2022-06-30 16:39:26 +02:00
|
|
|
nextPage,
|
2022-06-29 20:11:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-28 23:39:21 +02:00
|
|
|
export async function getScopedConfig(db: any, params: any) {
|
2021-05-06 11:51:21 +02:00
|
|
|
const configDoc = await getScopedFullConfig(db, params)
|
|
|
|
return configDoc && configDoc.config ? configDoc.config : configDoc
|
|
|
|
}
|