Merge branch 'develop' of github.com:Budibase/budibase into feature/cloud-export

This commit is contained in:
mike12345567 2021-09-29 12:57:14 +01:00
commit 1f127939af
28 changed files with 110 additions and 115 deletions

View File

@ -37,5 +37,5 @@ dependencies:
condition: services.couchdb.enabled condition: services.couchdb.enabled
- name: ingress-nginx - name: ingress-nginx
version: 3.35.0 version: 3.35.0
repository: https://github.com/kubernetes/ingress-nginx repository: https://kubernetes.github.io/ingress-nginx
condition: services.ingress.nginx condition: services.ingress.nginx

View File

@ -94,6 +94,8 @@ spec:
value: {{ .Values.globals.sentryDSN }} value: {{ .Values.globals.sentryDSN }}
- name: WORKER_URL - name: WORKER_URL
value: worker-service:{{ .Values.services.worker.port }} value: worker-service:{{ .Values.services.worker.port }}
- name: COOKIE_DOMAIN
value: {{ .Values.globals.cookieDomain | quote }}
image: budibase/apps image: budibase/apps
imagePullPolicy: Always imagePullPolicy: Always
name: bbapps name: bbapps

View File

@ -89,6 +89,8 @@ spec:
value: {{ .Values.globals.selfHosted | quote }} value: {{ .Values.globals.selfHosted | quote }}
- name: ACCOUNT_PORTAL_URL - name: ACCOUNT_PORTAL_URL
value: {{ .Values.globals.accountPortalUrl | quote }} value: {{ .Values.globals.accountPortalUrl | quote }}
- name: COOKIE_DOMAIN
value: {{ .Values.globals.cookieDomain | quote }}
image: budibase/worker image: budibase/worker
imagePullPolicy: Always imagePullPolicy: Always
name: bbworker name: bbworker

View File

@ -90,6 +90,7 @@ globals:
logLevel: info logLevel: info
selfHosted: 1 selfHosted: 1
accountPortalUrL: "" accountPortalUrL: ""
cookieDomain: ""
createSecrets: true # creates an internal API key, JWT secrets and redis password for you createSecrets: true # creates an internal API key, JWT secrets and redis password for you
# if createSecrets is set to false, you can hard-code your secrets here # if createSecrets is set to false, you can hard-code your secrets here

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -0,0 +1 @@
module.exports = require("./src/cloud/accounts")

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -22,6 +22,7 @@ module.exports = {
MULTI_TENANCY: process.env.MULTI_TENANCY, MULTI_TENANCY: process.env.MULTI_TENANCY,
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED), SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED),
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
isTest, isTest,
_set(key, value) { _set(key, value) {
process.env[key] = value process.env[key] = value

View File

@ -10,15 +10,10 @@ function finalise(
{ authenticated, user, internal, version, publicEndpoint } = {} { authenticated, user, internal, version, publicEndpoint } = {}
) { ) {
ctx.publicEndpoint = publicEndpoint || false ctx.publicEndpoint = publicEndpoint || false
console.log("Temp Auth Middleware: public endoint", ctx.publicEndpoint)
ctx.isAuthenticated = authenticated || false ctx.isAuthenticated = authenticated || false
console.log("Temp Auth Middleware: isAuthenticated", ctx.isAuthenticated)
ctx.user = user ctx.user = user
console.log("Temp Auth Middleware: user", ctx.user)
ctx.internal = internal || false ctx.internal = internal || false
console.log("Temp Auth Middleware: internal", ctx.internal)
ctx.version = version ctx.version = version
console.log("Temp Auth Middleware: version", ctx.version)
} }
/** /**
@ -32,50 +27,40 @@ module.exports = (
) => { ) => {
const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : []
return async (ctx, next) => { return async (ctx, next) => {
console.log("Temp Auth Middleware: Start auth middleware")
let publicEndpoint = false let publicEndpoint = false
const version = ctx.request.headers[Headers.API_VER] const version = ctx.request.headers[Headers.API_VER]
// the path is not authenticated // the path is not authenticated
const found = matches(ctx, noAuthOptions) const found = matches(ctx, noAuthOptions)
if (found) { if (found) {
console.log("Temp Auth Middleware: Public endpoint found")
publicEndpoint = true publicEndpoint = true
} }
try { try {
console.log("Temp Auth Middleware: Parsing cookie")
// check the actual user is authenticated first // check the actual user is authenticated first
const authCookie = getCookie(ctx, Cookies.Auth) const authCookie = getCookie(ctx, Cookies.Auth)
let authenticated = false, let authenticated = false,
user = null, user = null,
internal = false internal = false
if (authCookie) { if (authCookie) {
console.log("Temp Auth Middleware: Auth cookie found")
let error = null let error = null
const sessionId = authCookie.sessionId, const sessionId = authCookie.sessionId,
userId = authCookie.userId userId = authCookie.userId
console.log("Temp Auth Middleware: Getting session")
const session = await getSession(userId, sessionId) const session = await getSession(userId, sessionId)
if (!session) { if (!session) {
error = "No session found" error = "No session found"
} else { } else {
try { try {
console.log("Temp Auth Middleware: Getting user")
if (opts && opts.populateUser) { if (opts && opts.populateUser) {
console.log("Temp Auth Middleware: Populate user function found")
user = await getUser( user = await getUser(
userId, userId,
session.tenantId, session.tenantId,
opts.populateUser(ctx) opts.populateUser(ctx)
) )
} else { } else {
console.log("Temp Auth Middleware: Getting user from DB")
user = await getUser(userId, session.tenantId) user = await getUser(userId, session.tenantId)
} }
delete user.password delete user.password
console.log("Temp Auth Middleware: User is authenticated")
authenticated = true authenticated = true
} catch (err) { } catch (err) {
console.log("Temp Auth Middleware: Holy shit there was an error")
error = err error = err
} }
} }
@ -84,7 +69,6 @@ module.exports = (
// remove the cookie as the user does not exist anymore // remove the cookie as the user does not exist anymore
clearCookie(ctx, Cookies.Auth) clearCookie(ctx, Cookies.Auth)
} else { } else {
console.log("Temp Auth Middleware: No error")
// make sure we denote that the session is still in use // make sure we denote that the session is still in use
await updateSessionTTL(session) await updateSessionTTL(session)
} }
@ -103,23 +87,14 @@ module.exports = (
if (authenticated !== true) { if (authenticated !== true) {
authenticated = false authenticated = false
} }
console.log("Temp Auth Middleware: Auth status", {
authenticated,
user,
internal,
version,
publicEndpoint,
})
// isAuthenticated is a function, so use a variable to be able to check authed state // isAuthenticated is a function, so use a variable to be able to check authed state
finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) finalise(ctx, { authenticated, user, internal, version, publicEndpoint })
return next() return next()
} catch (err) { } catch (err) {
console.log("Temp Auth Middleware: Error:", err)
// allow configuring for public access // allow configuring for public access
if ((opts && opts.publicAllowed) || publicEndpoint) { if ((opts && opts.publicAllowed) || publicEndpoint) {
finalise(ctx, { authenticated: false, version, publicEndpoint }) finalise(ctx, { authenticated: false, version, publicEndpoint })
} else { } else {
console.log("Temp Auth Middleware: Throwing error status", err.status)
ctx.throw(err.status || 403, err) ctx.throw(err.status || 403, err)
} }
} }

View File

@ -53,6 +53,11 @@ exports.setTenantId = (
// processed later in the chain // processed later in the chain
tenantId = user.tenantId || header || tenantId tenantId = user.tenantId || header || tenantId
// Set the tenantId from the subdomain
if (!tenantId) {
tenantId = ctx.subdomains && ctx.subdomains[0]
}
if (!tenantId && !allowNoTenant) { if (!tenantId && !allowNoTenant) {
ctx.throw(403, "Tenant id not set") ctx.throw(403, "Tenant id not set")
} }

View File

@ -4,6 +4,7 @@ const { options } = require("./middleware/passport/jwt")
const { createUserEmailView } = require("./db/views") const { createUserEmailView } = require("./db/views")
const { Headers } = require("./constants") const { Headers } = require("./constants")
const { getGlobalDB } = require("./tenancy") const { getGlobalDB } = require("./tenancy")
const environment = require("./environment")
const APP_PREFIX = DocumentTypes.APP + SEPARATOR const APP_PREFIX = DocumentTypes.APP + SEPARATOR
@ -70,12 +71,19 @@ exports.setCookie = (ctx, value, name = "builder") => {
ctx.cookies.set(name) ctx.cookies.set(name)
} else { } else {
value = jwt.sign(value, options.secretOrKey) value = jwt.sign(value, options.secretOrKey)
ctx.cookies.set(name, value, {
const config = {
maxAge: Number.MAX_SAFE_INTEGER, maxAge: Number.MAX_SAFE_INTEGER,
path: "/", path: "/",
httpOnly: false, httpOnly: false,
overwrite: true, overwrite: true,
}) }
if (environment.COOKIE_DOMAIN) {
config.domain = environment.COOKIE_DOMAIN
}
ctx.cookies.set(name, value, config)
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.143-alpha.1", "@budibase/bbui": "^0.9.144-alpha.0",
"@budibase/client": "^0.9.143-alpha.1", "@budibase/client": "^0.9.144-alpha.0",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.143-alpha.1", "@budibase/string-templates": "^0.9.144-alpha.0",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -3,8 +3,6 @@ import PosthogClient from "./PosthogClient"
import IntercomClient from "./IntercomClient" import IntercomClient from "./IntercomClient"
import SentryClient from "./SentryClient" import SentryClient from "./SentryClient"
import { Events } from "./constants" import { Events } from "./constants"
import { auth } from "stores/portal"
import { get } from "svelte/store"
const posthog = new PosthogClient( const posthog = new PosthogClient(
process.env.POSTHOG_TOKEN, process.env.POSTHOG_TOKEN,
@ -19,27 +17,13 @@ class AnalyticsHub {
} }
async activate() { async activate() {
// Setting the analytics env var off in the backend overrides org/tenant settings
const analyticsStatus = await api.get("/api/analytics") const analyticsStatus = await api.get("/api/analytics")
const json = await analyticsStatus.json() const json = await analyticsStatus.json()
// Multitenancy disabled on the backend // Analytics disabled
if (!json.enabled) return if (!json.enabled) return
const tenantId = get(auth).tenantId
if (tenantId) {
const res = await api.get(
`/api/global/configs/public?tenantId=${tenantId}`
)
const orgJson = await res.json()
// analytics opted out for the tenant
if (orgJson.config?.analytics === false) return
}
this.clients.forEach(client => client.init()) this.clients.forEach(client => client.init())
this.enabled = true
} }
identify(id, metadata) { identify(id, metadata) {

View File

@ -8,7 +8,7 @@
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviderComponents(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"RefreshDataProvider" "RefreshDatasource"
) )
</script> </script>

View File

@ -133,7 +133,7 @@
/> />
{:else if ["string", "longform", "number"].includes(filter.type)} {:else if ["string", "longform", "number"].includes(filter.type)}
<Input disabled={filter.noValue} bind:value={filter.value} /> <Input disabled={filter.noValue} bind:value={filter.value} />
{:else if filter.type === "options" || "array"} {:else if ["options", "array"].includes(filter.type)}
<Combobox <Combobox
disabled={filter.noValue} disabled={filter.noValue}
options={getFieldOptions(filter.field)} options={getFieldOptions(filter.field)}

View File

@ -12,7 +12,12 @@
} }
// redirect to account portal for authentication in the cloud // redirect to account portal for authentication in the cloud
if (!$auth.user && $admin.cloud && $admin.accountPortalUrl) { if (
!$auth.user &&
$admin.cloud &&
$admin.accountPortalUrl &&
!$admin?.checklist?.sso?.checked
) {
window.location.href = $admin.accountPortalUrl window.location.href = $admin.accountPortalUrl
} }
}) })

View File

@ -7,13 +7,11 @@
Divider, Divider,
Label, Label,
Input, Input,
Toggle,
Dropzone, Dropzone,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { auth, organisation } from "stores/portal" import { auth, organisation, admin } from "stores/portal"
import { post } from "builderStore/api" import { post } from "builderStore/api"
import analytics from "analytics"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
@ -25,7 +23,6 @@
} }
const values = writable({ const values = writable({
analytics: analytics.enabled,
company: $organisation.company, company: $organisation.company,
platformUrl: $organisation.platformUrl, platformUrl: $organisation.platformUrl,
logo: $organisation.logoUrl logo: $organisation.logoUrl
@ -57,7 +54,6 @@
const config = { const config = {
company: $values.company ?? "", company: $values.company ?? "",
platformUrl: $values.platformUrl ?? "", platformUrl: $values.platformUrl ?? "",
analytics: $values.analytics,
} }
// remove logo if required // remove logo if required
if (!$values.logo) { if (!$values.logo) {
@ -112,34 +108,22 @@
</div> </div>
</div> </div>
</div> </div>
<Divider size="S" /> {#if !$admin.cloud}
<Layout gap="XS" noPadding> <Divider size="S" />
<Heading size="S">Platform</Heading>
<Body size="S">Here you can set up general platform settings.</Body>
</Layout>
<div class="fields">
<div class="field">
<Label size="L">Platform URL</Label>
<Input thin bind:value={$values.platformUrl} />
</div>
</div>
<Divider size="S" />
<Layout gap="S" noPadding>
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Heading size="S">Analytics</Heading> <Heading size="S">Platform</Heading>
<Body size="S"> <Body size="S">Here you can set up general platform settings.</Body>
If you would like to send analytics that help us make Budibase better,
please let us know below.
</Body>
</Layout> </Layout>
<Toggle <div class="fields">
text="Send Analytics to Budibase" <div class="field">
bind:value={$values.analytics} <Label size="L">Platform URL</Label>
/> <Input thin bind:value={$values.platformUrl} />
<div> </div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div> </div>
</Layout> {/if}
<div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div>
</Layout> </Layout>
{/if} {/if}

View File

@ -54,15 +54,13 @@ export function createAuthStore() {
if (user) { if (user) {
analytics.activate().then(() => { analytics.activate().then(() => {
analytics.identify(user._id, user) analytics.identify(user._id, user)
if (user.size === "100+" || user.size === "10000+") { analytics.showChat({
analytics.showChat({ email: user.email,
email: user.email, created_at: user.createdAt || Date.now(),
created_at: user.createdAt || Date.now(), name: user.name,
name: user.name, user_id: user._id,
user_id: user._id, tenant: user.tenantId,
tenant: user.tenantId, })
})
}
}) })
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.143-alpha.1", "@budibase/bbui": "^0.9.144-alpha.0",
"@budibase/standard-components": "^0.9.139", "@budibase/standard-components": "^0.9.139",
"@budibase/string-templates": "^0.9.143-alpha.1", "@budibase/string-templates": "^0.9.144-alpha.0",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -25,7 +25,9 @@
"lint:fix": "yarn run format && yarn run lint", "lint:fix": "yarn run format && yarn run lint",
"initialise": "node scripts/initialise.js", "initialise": "node scripts/initialise.js",
"multi:enable": "node scripts/multiTenancy.js enable", "multi:enable": "node scripts/multiTenancy.js enable",
"multi:disable": "node scripts/multiTenancy.js disable" "multi:disable": "node scripts/multiTenancy.js disable",
"selfhost:enable": "node scripts/selfhost.js enable",
"selfhost:disable": "node scripts/selfhost.js disable"
}, },
"jest": { "jest": {
"preset": "ts-jest", "preset": "ts-jest",
@ -62,9 +64,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.143-alpha.1", "@budibase/auth": "^0.9.144-alpha.0",
"@budibase/client": "^0.9.143-alpha.1", "@budibase/client": "^0.9.144-alpha.0",
"@budibase/string-templates": "^0.9.143-alpha.1", "@budibase/string-templates": "^0.9.144-alpha.0",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",

View File

@ -126,7 +126,7 @@ module PostgresModule {
private readonly config: PostgresConfig private readonly config: PostgresConfig
COLUMNS_SQL = COLUMNS_SQL =
"select * from information_schema.columns where table_schema = 'public'" "select * from information_schema.columns where not table_schema = 'information_schema' and not table_schema = 'pg_catalog'"
PRIMARY_KEYS_SQL = ` PRIMARY_KEYS_SQL = `
select tc.table_schema, tc.table_name, kc.column_name as primary_key select tc.table_schema, tc.table_name, kc.column_name as primary_key

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.143-alpha.1", "version": "0.9.144-alpha.0",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -25,8 +25,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.143-alpha.1", "@budibase/auth": "^0.9.144-alpha.0",
"@budibase/string-templates": "^0.9.143-alpha.1", "@budibase/string-templates": "^0.9.144-alpha.0",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",
"aws-sdk": "^2.811.0", "aws-sdk": "^2.811.0",

View File

@ -11,6 +11,7 @@ const { sendEmail } = require("../../../utilities/email")
const { user: userCache } = require("@budibase/auth/cache") const { user: userCache } = require("@budibase/auth/cache")
const { invalidateSessions } = require("@budibase/auth/sessions") const { invalidateSessions } = require("@budibase/auth/sessions")
const CouchDB = require("../../../db") const CouchDB = require("../../../db")
const accounts = require("@budibase/auth/accounts")
const { const {
getGlobalDB, getGlobalDB,
getTenantId, getTenantId,
@ -49,10 +50,27 @@ async function saveUser(
// make sure another user isn't using the same email // make sure another user isn't using the same email
let dbUser let dbUser
if (email) { if (email) {
// check budibase users inside the tenant
dbUser = await getGlobalUserByEmail(email) dbUser = await getGlobalUserByEmail(email)
if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) {
throw "Email address already in use." throw "Email address already in use."
} }
// check budibase users in other tenants
if (env.MULTI_TENANCY) {
dbUser = await getTenantUser(email)
if (dbUser != null) {
throw "Email address already in use."
}
}
// check root account users in account portal
if (!env.SELF_HOSTED) {
const account = await accounts.getAccount(email)
if (account) {
throw "Email address already in use."
}
}
} else { } else {
dbUser = await db.get(_id) dbUser = await db.get(_id)
} }
@ -267,13 +285,22 @@ exports.find = async ctx => {
ctx.body = user ctx.body = user
} }
exports.tenantUserLookup = async ctx => { // lookup, could be email or userId, either will return a doc
const id = ctx.params.id const getTenantUser = async identifier => {
// lookup, could be email or userId, either will return a doc
const db = new CouchDB(PLATFORM_INFO_DB) const db = new CouchDB(PLATFORM_INFO_DB)
try { try {
ctx.body = await db.get(id) return await db.get(identifier)
} catch (err) { } catch (err) {
return null
}
}
exports.tenantUserLookup = async ctx => {
const id = ctx.params.id
const user = await getTenantUser(id)
if (user) {
ctx.body = user
} else {
ctx.throw(400, "No tenant user found.") ctx.throw(400, "No tenant user found.")
} }
} }

View File

@ -19,7 +19,7 @@
} }
a { a {
color: #3869D4; color: #3869D4 !important;
} }
a img { a img {
@ -115,8 +115,8 @@
border-bottom: 10px solid #3869D4; border-bottom: 10px solid #3869D4;
border-left: 18px solid #3869D4; border-left: 18px solid #3869D4;
display: inline-block; display: inline-block;
color: #FFF; color: #FFF !important;
text-decoration: none; text-decoration: none !important;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
-webkit-text-size-adjust: none; -webkit-text-size-adjust: none;