diff --git a/hosting/nginx.dev.conf.hbs b/hosting/nginx.dev.conf.hbs index 7bc8100e45..93a07435e5 100644 --- a/hosting/nginx.dev.conf.hbs +++ b/hosting/nginx.dev.conf.hbs @@ -58,12 +58,15 @@ http { } location ~ ^/api/(system|admin|global)/ { - proxy_pass http://worker-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://worker-service; } location /api/backups/ { @@ -78,60 +81,78 @@ http { location /api/ { proxy_read_timeout 120s; proxy_connect_timeout 120s; - proxy_send_timeout 120s; - proxy_pass http://app-service; + proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location = / { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /app_ { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /app { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /builder { - proxy_pass http://builder; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://builder; rewrite ^/builder(.*)$ /builder/$1 break; } location /builder/ { - proxy_pass http://builder; - proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; + + proxy_pass http://builder; } location /vite/ { diff --git a/hosting/nginx.prod.conf.hbs b/hosting/nginx.prod.conf.hbs index aa79fd92ad..15d7c6823e 100644 --- a/hosting/nginx.prod.conf.hbs +++ b/hosting/nginx.prod.conf.hbs @@ -100,18 +100,25 @@ http { location ~ ^/(builder|app_) { proxy_http_version 1.1; + proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_pass http://$apps:4002; } location ~ ^/api/(system|admin|global)/ { + proxy_set_header Host $host; + proxy_pass http://$worker:4003; } location /worker/ { + proxy_set_header Host $host; + proxy_pass http://$worker:4003; rewrite ^/worker/(.*)$ /$1 break; } @@ -139,6 +146,7 @@ http { proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; proxy_pass http://$apps:4002; } @@ -158,6 +166,7 @@ http { proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; proxy_pass http://$apps:4002; } diff --git a/package.json b/package.json index 7733a6df95..592e72dde7 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,8 @@ "env:multi:disable": "lerna run env:multi:disable", "env:selfhost:enable": "lerna run env:selfhost:enable", "env:selfhost:disable": "lerna run env:selfhost:disable", - "env:localdomain:enable": "lerna run env:localdomain:enable", - "env:localdomain:disable": "lerna run env:localdomain:disable", + "env:localdomain:enable": "./scripts/localdomain.sh enable", + "env:localdomain:disable": "./scripts/localdomain.sh disable", "env:account:enable": "lerna run env:account:enable", "env:account:disable": "lerna run env:account:disable", "mode:self": "yarn env:selfhost:enable && yarn env:multi:disable && yarn env:account:disable", diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index c04da5da4f..ee100cfa7b 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -15,6 +15,7 @@ import { getAppMetadata } from "../cache/appMetadata" import { isDevApp, isDevAppID, getProdAppID } from "./conversions" import { APP_PREFIX } from "./constants" import * as events from "../events" +import { App } from "@budibase/types" export * from "./constants" export * from "./conversions" @@ -301,7 +302,12 @@ export async function getAllDbs(opts = { efficient: false }) { * * @return {Promise} returns the app information document stored in each app database. */ -export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { +export async function getAllApps({ + dev, + all, + idsOnly, + efficient, +}: any = {}): Promise { let tenantId = getTenantId() if (!env.MULTI_TENANCY && !tenantId) { tenantId = DEFAULT_TENANT_ID @@ -373,18 +379,16 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { * Utility function for getAllApps but filters to production apps only. */ export async function getProdAppIDs() { - return (await getAllApps({ idsOnly: true })).filter( - (id: any) => !isDevAppID(id) - ) + const apps = (await getAllApps({ idsOnly: true })) as string[] + return apps.filter((id: any) => !isDevAppID(id)) } /** * Utility function for the inverse of above. */ export async function getDevAppIDs() { - return (await getAllApps({ idsOnly: true })).filter((id: any) => - isDevAppID(id) - ) + const apps = (await getAllApps({ idsOnly: true })) as string[] + return apps.filter((id: any) => isDevAppID(id)) } export async function dbExists(dbName: any) { diff --git a/packages/backend-core/src/middleware/tenancy.js b/packages/backend-core/src/middleware/tenancy.js deleted file mode 100644 index 8083322b29..0000000000 --- a/packages/backend-core/src/middleware/tenancy.js +++ /dev/null @@ -1,52 +0,0 @@ -const { doInTenant, isMultiTenant, DEFAULT_TENANT_ID } = require("../tenancy") -const { buildMatcherRegex, matches } = require("./matchers") -const { Headers } = require("../constants") - -const getTenantID = (ctx, opts = { allowQs: false, allowNoTenant: false }) => { - // exit early if not multi-tenant - if (!isMultiTenant()) { - return DEFAULT_TENANT_ID - } - - let tenantId - const allowQs = opts && opts.allowQs - const allowNoTenant = opts && opts.allowNoTenant - const header = ctx.request.headers[Headers.TENANT_ID] - const user = ctx.user || {} - if (allowQs) { - const query = ctx.request.query || {} - tenantId = query.tenantId - } - // override query string (if allowed) by user, or header - // URL params cannot be used in a middleware, as they are - // processed later in the chain - tenantId = user.tenantId || header || tenantId - - // Set the tenantId from the subdomain - if (!tenantId) { - tenantId = ctx.subdomains && ctx.subdomains[0] - } - - if (!tenantId && !allowNoTenant) { - ctx.throw(403, "Tenant id not set") - } - - return tenantId -} - -module.exports = ( - allowQueryStringPatterns, - noTenancyPatterns, - opts = { noTenancyRequired: false } -) => { - const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) - const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) - - return async function (ctx, next) { - const allowNoTenant = - opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) - const allowQs = !!matches(ctx, allowQsOptions) - const tenantId = getTenantID(ctx, { allowQs, allowNoTenant }) - return doInTenant(tenantId, next) - } -} diff --git a/packages/backend-core/src/middleware/tenancy.ts b/packages/backend-core/src/middleware/tenancy.ts new file mode 100644 index 0000000000..880daa9b5b --- /dev/null +++ b/packages/backend-core/src/middleware/tenancy.ts @@ -0,0 +1,35 @@ +import { doInTenant, getTenantIDFromCtx } from "../tenancy" +import { buildMatcherRegex, matches } from "./matchers" +import { + BBContext, + EndpointMatcher, + GetTenantIdOptions, + TenantResolutionStrategy, +} from "@budibase/types" + +const tenancy = ( + allowQueryStringPatterns: EndpointMatcher[], + noTenancyPatterns: EndpointMatcher, + opts = { noTenancyRequired: false } +) => { + const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) + const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) + + return async function (ctx: BBContext, next: any) { + const allowNoTenant = + opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) + const tenantOpts: GetTenantIdOptions = { + allowNoTenant, + } + + const allowQs = !!matches(ctx, allowQsOptions) + if (!allowQs) { + tenantOpts.excludeStrategies = [TenantResolutionStrategy.QUERY] + } + + const tenantId = getTenantIDFromCtx(ctx, tenantOpts) + return doInTenant(tenantId, next) + } +} + +export = tenancy diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts index 90a12acec2..bd50fecc87 100644 --- a/packages/backend-core/src/migrations/migrations.ts +++ b/packages/backend-core/src/migrations/migrations.ts @@ -12,6 +12,7 @@ import { MigrationOptions, MigrationType, MigrationNoOpOptions, + App, } from "@budibase/types" export const getMigrationsDoc = async (db: any) => { @@ -55,14 +56,17 @@ export const runMigration = async ( } // get the db to store the migration in - let dbNames + let dbNames: string[] if (migrationType === MigrationType.GLOBAL) { dbNames = [getGlobalDBName()] } else if (migrationType === MigrationType.APP) { if (options.noOp) { + if (!options.noOp.appId) { + throw new Error("appId is required for noOp app migration") + } dbNames = [options.noOp.appId] } else { - const apps = await getAllApps(migration.appOpts) + const apps = (await getAllApps(migration.appOpts)) as App[] dbNames = apps.map(app => app.appId) } } else if (migrationType === MigrationType.INSTALLATION) { diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index ad5c6b5287..0d3bf54d07 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -9,7 +9,13 @@ import { getTenantIDFromAppID, } from "../context" import env from "../environment" -import { PlatformUser } from "@budibase/types" +import { + BBContext, + PlatformUser, + TenantResolutionStrategy, + GetTenantIdOptions, +} from "@budibase/types" +import { Headers } from "../constants" const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name @@ -144,3 +150,82 @@ export const getTenantIds = async () => { return (tenants && tenants.tenantIds) || [] }) } + +const ALL_STRATEGIES = Object.values(TenantResolutionStrategy) + +export const getTenantIDFromCtx = ( + ctx: BBContext, + opts: GetTenantIdOptions +): string | null => { + // exit early if not multi-tenant + if (!isMultiTenant()) { + return DEFAULT_TENANT_ID + } + + // opt defaults + if (opts.allowNoTenant === undefined) { + opts.allowNoTenant = false + } + if (!opts.includeStrategies) { + opts.includeStrategies = ALL_STRATEGIES + } + if (!opts.excludeStrategies) { + opts.excludeStrategies = [] + } + + const isAllowed = (strategy: TenantResolutionStrategy) => { + // excluded takes precedence + if (opts.excludeStrategies?.includes(strategy)) { + return false + } + if (opts.includeStrategies?.includes(strategy)) { + return true + } + } + + // always use user first + if (isAllowed(TenantResolutionStrategy.USER)) { + const userTenantId = ctx.user?.tenantId + if (userTenantId) { + return userTenantId + } + } + + // header + if (isAllowed(TenantResolutionStrategy.HEADER)) { + const headerTenantId = ctx.request.headers[Headers.TENANT_ID] + if (headerTenantId) { + return headerTenantId as string + } + } + + // query param + if (isAllowed(TenantResolutionStrategy.QUERY)) { + const queryTenantId = ctx.request.query.tenantId + if (queryTenantId) { + return queryTenantId as string + } + } + + // subdomain + if (isAllowed(TenantResolutionStrategy.SUBDOMAIN)) { + // e.g. budibase.app or local.com:10000 + const platformHost = new URL(env.PLATFORM_URL).host.split(":")[0] + // e.g. tenant.budibase.app or tenant.local.com + const requestHost = ctx.host + // parse the tenant id from the difference + const tenantId = requestHost.substring( + 0, + requestHost.indexOf(`.${platformHost}`) + ) + if (tenantId) { + return tenantId + } + } + + if (!opts.allowNoTenant) { + ctx.throw(403, "Tenant id not set") + } + + return null +} diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.ts similarity index 66% rename from packages/backend-core/src/utils.js rename to packages/backend-core/src/utils.ts index 6b59c7cb72..03b4747ee3 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.ts @@ -1,38 +1,41 @@ -const { DocumentType, SEPARATOR, ViewName, getAllApps } = require("./db/utils") +import { DocumentType, SEPARATOR, ViewName, getAllApps } from "./db/utils" const jwt = require("jsonwebtoken") -const { options } = require("./middleware/passport/jwt") -const { queryGlobalView } = require("./db/views") -const { Headers, Cookies, MAX_VALID_DATE } = require("./constants") -const env = require("./environment") -const userCache = require("./cache/user") -const { - getSessionsForUser, - invalidateSessions, -} = require("./security/sessions") -const events = require("./events") -const tenancy = require("./tenancy") +import { options } from "./middleware/passport/jwt" +import { queryGlobalView } from "./db/views" +import { Headers, Cookies, MAX_VALID_DATE } from "./constants" +import env from "./environment" +import userCache from "./cache/user" +import { getSessionsForUser, invalidateSessions } from "./security/sessions" +import * as events from "./events" +import tenancy from "./tenancy" +import { App, BBContext, TenantResolutionStrategy } from "@budibase/types" +import { SetOption } from "cookies" const APP_PREFIX = DocumentType.APP + SEPARATOR const PROD_APP_PREFIX = "/app/" -function confirmAppId(possibleAppId) { +function confirmAppId(possibleAppId: string | undefined) { return possibleAppId && possibleAppId.startsWith(APP_PREFIX) ? possibleAppId : undefined } -async function resolveAppUrl(ctx) { +async function resolveAppUrl(ctx: BBContext) { const appUrl = ctx.path.split("/")[2] let possibleAppUrl = `/${appUrl.toLowerCase()}` let tenantId = tenancy.getTenantId() - if (!env.SELF_HOSTED && ctx.subdomains.length) { - // always use the tenant id from the url in cloud - tenantId = ctx.subdomains[0] + if (!env.SELF_HOSTED) { + // always use the tenant id from the subdomain in cloud + // this ensures the logged-in user tenant id doesn't overwrite + // e.g. in the case of viewing a public app while already logged-in to another tenant + tenantId = tenancy.getTenantIDFromCtx(ctx, { + includeStrategies: [TenantResolutionStrategy.SUBDOMAIN], + }) } // search prod apps for a url that matches - const apps = await tenancy.doInTenant(tenantId, () => + const apps: App[] = await tenancy.doInTenant(tenantId, () => getAllApps({ dev: false }) ) const app = apps.filter( @@ -42,7 +45,7 @@ async function resolveAppUrl(ctx) { return app && app.appId ? app.appId : undefined } -exports.isServingApp = ctx => { +export const isServingApp = (ctx: BBContext) => { // dev app if (ctx.path.startsWith(`/${APP_PREFIX}`)) { return true @@ -59,12 +62,12 @@ exports.isServingApp = ctx => { * @param {object} ctx The main request body to look through. * @returns {string|undefined} If an appId was found it will be returned. */ -exports.getAppIdFromCtx = async ctx => { +export const getAppIdFromCtx = async (ctx: BBContext) => { // look in headers const options = [ctx.headers[Headers.APP_ID]] let appId for (let option of options) { - appId = confirmAppId(option) + appId = confirmAppId(option as string) if (appId) { break } @@ -95,7 +98,7 @@ exports.getAppIdFromCtx = async ctx => { * opens the contents of the specified encrypted JWT. * @return {object} the contents of the token. */ -exports.openJwt = token => { +export const openJwt = (token: string) => { if (!token) { return token } @@ -107,14 +110,14 @@ exports.openJwt = token => { * @param {object} ctx The request which is to be manipulated. * @param {string} name The name of the cookie to get. */ -exports.getCookie = (ctx, name) => { +export const getCookie = (ctx: BBContext, name: string) => { const cookie = ctx.cookies.get(name) if (!cookie) { return cookie } - return exports.openJwt(cookie) + return openJwt(cookie) } /** @@ -124,12 +127,17 @@ exports.getCookie = (ctx, name) => { * @param {string|object} value The value of cookie which will be set. * @param {object} opts options like whether to sign. */ -exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { +export const setCookie = ( + ctx: BBContext, + value: any, + name = "builder", + opts = { sign: true } +) => { if (value && opts && opts.sign) { value = jwt.sign(value, options.secretOrKey) } - const config = { + const config: SetOption = { expires: MAX_VALID_DATE, path: "/", httpOnly: false, @@ -146,8 +154,8 @@ exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { /** * Utility function, simply calls setCookie with an empty string for value */ -exports.clearCookie = (ctx, name) => { - exports.setCookie(ctx, null, name) +export const clearCookie = (ctx: BBContext, name: string) => { + setCookie(ctx, null, name) } /** @@ -156,7 +164,7 @@ exports.clearCookie = (ctx, name) => { * @param {object} ctx The koa context object to be tested. * @return {boolean} returns true if the call is from the client lib (a built app rather than the builder). */ -exports.isClient = ctx => { +export const isClient = (ctx: BBContext) => { return ctx.headers[Headers.TYPE] === "client" } @@ -176,18 +184,28 @@ const getBuilders = async () => { } } -exports.getBuildersCount = async () => { +export const getBuildersCount = async () => { const builders = await getBuilders() return builders.length } +interface PlatformLogoutOpts { + ctx: BBContext + userId: string + keepActiveSession: boolean +} + /** * Logs a user out from budibase. Re-used across account portal and builder. */ -exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { +export const platformLogout = async (opts: PlatformLogoutOpts) => { + const ctx = opts.ctx + const userId = opts.userId + const keepActiveSession = opts.keepActiveSession + if (!ctx) throw new Error("Koa context must be supplied to logout.") - const currentSession = exports.getCookie(ctx, Cookies.Auth) + const currentSession = getCookie(ctx, Cookies.Auth) let sessions = await getSessionsForUser(userId) if (keepActiveSession) { @@ -196,8 +214,8 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { ) } else { // clear cookies - exports.clearCookie(ctx, Cookies.Auth) - exports.clearCookie(ctx, Cookies.CurrentApp) + clearCookie(ctx, Cookies.Auth) + clearCookie(ctx, Cookies.CurrentApp) } const sessionIds = sessions.map(({ sessionId }) => sessionId) @@ -206,6 +224,6 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { await userCache.invalidateUser(userId) } -exports.timeout = timeMs => { +export const timeout = (timeMs: number) => { return new Promise(resolve => setTimeout(resolve, timeMs)) } diff --git a/packages/server/scripts/localdomain.js b/packages/server/scripts/localdomain.js index 7dd1c083b4..9317538e9f 100644 --- a/packages/server/scripts/localdomain.js +++ b/packages/server/scripts/localdomain.js @@ -2,6 +2,36 @@ const updateDotEnv = require("update-dotenv") const arg = process.argv.slice(2)[0] +const isEnable = arg === "enable" + +let domain = process.argv.slice(2)[1] +if (!domain) { + domain = "local.com" +} + +const getAccountPortalUrl = () => { + if (isEnable) { + return `http://account.${domain}:10001` + } else { + return `http://localhost:10001` + } +} + +const getBudibaseUrl = () => { + if (isEnable) { + return `http://${domain}:10000` + } else { + return `http://localhost:10000` + } +} + +const getCookieDomain = () => { + if (isEnable) { + return `.${domain}` + } else { + return "" + } +} /** * For testing multi tenancy sub domains locally. @@ -16,9 +46,7 @@ const arg = process.argv.slice(2)[0] * 127.0.0.1 t2.local.com */ updateDotEnv({ - ACCOUNT_PORTAL_URL: - arg === "enable" - ? "http://account.local.com:10001" - : "http://localhost:10001", - COOKIE_DOMAIN: arg === "enable" ? ".local.com" : "", -}).then(() => console.log("Updated worker!")) + ACCOUNT_PORTAL_URL: getAccountPortalUrl(), + COOKIE_DOMAIN: getCookieDomain(), + PLATFORM_URL: getBudibaseUrl(), +}).then(() => console.log("Updated server!")) diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index d7be61c130..e3a96b77dc 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -149,7 +149,7 @@ export const run = async (db: any) => { } try { - const allApps: App[] = await dbUtils.getAllApps({ dev: true }) + const allApps = (await dbUtils.getAllApps({ dev: true })) as App[] totals.apps = allApps.length totals.usage = await quotas.backfill(allApps) diff --git a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts index 0b123d2357..e5c8a1743c 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts @@ -2,11 +2,11 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { getAllApps } from "@budibase/backend-core/db" import { getUniqueRows } from "../../../utilities/usageQuota/rows" import { quotas } from "@budibase/pro" -import { StaticQuotaName, QuotaUsageType } from "@budibase/types" +import { StaticQuotaName, QuotaUsageType, App } from "@budibase/types" export const run = async () => { // get all rows in all apps - const allApps = await getAllApps({ all: true }) + const allApps = (await getAllApps({ all: true })) as App[] const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const { appRows } = await getUniqueRows(appIds) diff --git a/packages/types/src/sdk/index.ts b/packages/types/src/sdk/index.ts index 724b152303..a32c8e2077 100644 --- a/packages/types/src/sdk/index.ts +++ b/packages/types/src/sdk/index.ts @@ -9,3 +9,4 @@ export * from "./koa" export * from "./auth" export * from "./locks" export * from "./db" +export * from "./middleware" diff --git a/packages/types/src/sdk/middleware/index.ts b/packages/types/src/sdk/middleware/index.ts new file mode 100644 index 0000000000..bc4220e329 --- /dev/null +++ b/packages/types/src/sdk/middleware/index.ts @@ -0,0 +1,2 @@ +export * from "./matchers" +export * from "./tenancy" diff --git a/packages/types/src/sdk/middleware/matchers.ts b/packages/types/src/sdk/middleware/matchers.ts new file mode 100644 index 0000000000..026c3924b0 --- /dev/null +++ b/packages/types/src/sdk/middleware/matchers.ts @@ -0,0 +1,4 @@ +export interface EndpointMatcher { + route: string + method: string +} diff --git a/packages/types/src/sdk/middleware/tenancy.ts b/packages/types/src/sdk/middleware/tenancy.ts new file mode 100644 index 0000000000..dd94b680c3 --- /dev/null +++ b/packages/types/src/sdk/middleware/tenancy.ts @@ -0,0 +1,12 @@ +export interface GetTenantIdOptions { + allowNoTenant?: boolean + excludeStrategies?: TenantResolutionStrategy[] + includeStrategies?: TenantResolutionStrategy[] +} + +export enum TenantResolutionStrategy { + USER = "user", + HEADER = "header", + QUERY = "query", + SUBDOMAIN = "subdomain", +} diff --git a/packages/worker/scripts/localdomain.js b/packages/worker/scripts/localdomain.js index 4e181628b2..985840b401 100644 --- a/packages/worker/scripts/localdomain.js +++ b/packages/worker/scripts/localdomain.js @@ -2,6 +2,36 @@ const updateDotEnv = require("update-dotenv") const arg = process.argv.slice(2)[0] +const isEnable = arg === "enable" + +let domain = process.argv.slice(2)[1] +if (!domain) { + domain = "local.com" +} + +const getAccountPortalUrl = () => { + if (isEnable) { + return `http://account.${domain}:10001` + } else { + return `http://localhost:10001` + } +} + +const getBudibaseUrl = () => { + if (isEnable) { + return `http://${domain}:10000` + } else { + return `http://localhost:10000` + } +} + +const getCookieDomain = () => { + if (isEnable) { + return `.${domain}` + } else { + return "" + } +} /** * For testing multi tenancy sub domains locally. @@ -16,11 +46,7 @@ const arg = process.argv.slice(2)[0] * 127.0.0.1 t2.local.com */ updateDotEnv({ - ACCOUNT_PORTAL_URL: - arg === "enable" - ? "http://account.local.com:10001" - : "http://localhost:10001", - COOKIE_DOMAIN: arg === "enable" ? ".local.com" : "", - PLATFORM_URL: - arg === "enable" ? "http://local.com:10000" : "http://localhost:10000", + ACCOUNT_PORTAL_URL: getAccountPortalUrl(), + COOKIE_DOMAIN: getCookieDomain(), + PLATFORM_URL: getBudibaseUrl(), }).then(() => console.log("Updated worker!")) diff --git a/scripts/localdomain.sh b/scripts/localdomain.sh new file mode 100755 index 0000000000..d32dbcc116 --- /dev/null +++ b/scripts/localdomain.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +enable=$1 +domain=$2 + +if [ "$enable" = "enable" ]; then + lerna run env:localdomain:enable -- "$domain" + cd ../account-portal + yarn env:localdomain:enable "$domain" + cd - +else + lerna run env:localdomain:disable + cd ../account-portal + yarn env:localdomain:disable + cd - +fi \ No newline at end of file