mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor: fix cloud dev (#3281)
This commit is contained in:
parent
0a3d4bb345
commit
afe39f6f46
6 changed files with 46 additions and 6 deletions
|
@ -16,7 +16,9 @@ import { log } from '../../../utils.js';
|
||||||
export const appendAdminConsoleRedirectUris = async (pool: CommonQueryMethods) => {
|
export const appendAdminConsoleRedirectUris = async (pool: CommonQueryMethods) => {
|
||||||
const redirectUris = new GlobalValues().cloudUrlSet
|
const redirectUris = new GlobalValues().cloudUrlSet
|
||||||
.deduplicated()
|
.deduplicated()
|
||||||
.map((endpoint) => appendPath(endpoint, defaultTenantId, 'callback'));
|
.flatMap((endpoint) =>
|
||||||
|
[defaultTenantId, adminTenantId].map((tenantId) => appendPath(endpoint, tenantId, 'callback'))
|
||||||
|
);
|
||||||
|
|
||||||
const metadataKey = sql.identifier(['oidc_client_metadata']);
|
const metadataKey = sql.identifier(['oidc_client_metadata']);
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,12 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppEndpoints = {
|
export type AppEndpoints = {
|
||||||
|
/**
|
||||||
|
* The Logto endpoint for the current tenant.
|
||||||
|
*
|
||||||
|
* Always use this value as the base URL when referring to the Logto URL of the current user's tenant.
|
||||||
|
*/
|
||||||
userEndpoint?: URL;
|
userEndpoint?: URL;
|
||||||
adminEndpoint?: URL;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppEndpointsContext = createContext<AppEndpoints>({});
|
export const AppEndpointsContext = createContext<AppEndpoints>({});
|
||||||
|
|
|
@ -119,7 +119,7 @@ describe('submit action', () => {
|
||||||
id: 'uid',
|
id: 'uid',
|
||||||
...upsertProfile,
|
...upsertProfile,
|
||||||
},
|
},
|
||||||
[]
|
['user']
|
||||||
);
|
);
|
||||||
expect(assignInteractionResults).toBeCalledWith(ctx, tenant.provider, {
|
expect(assignInteractionResults).toBeCalledWith(ctx, tenant.provider, {
|
||||||
login: { accountId: 'uid' },
|
login: { accountId: 'uid' },
|
||||||
|
|
|
@ -10,11 +10,13 @@ import {
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
import { conditional } from '@silverhand/essentials';
|
import { conditional } from '@silverhand/essentials';
|
||||||
|
|
||||||
|
import { EnvSet } from '#src/env-set/index.js';
|
||||||
import type { ConnectorLibrary } from '#src/libraries/connector.js';
|
import type { ConnectorLibrary } from '#src/libraries/connector.js';
|
||||||
import { assignInteractionResults } from '#src/libraries/session.js';
|
import { assignInteractionResults } from '#src/libraries/session.js';
|
||||||
import { encryptUserPassword } from '#src/libraries/user.js';
|
import { encryptUserPassword } from '#src/libraries/user.js';
|
||||||
import type { LogEntry } from '#src/middleware/koa-audit-log.js';
|
import type { LogEntry } from '#src/middleware/koa-audit-log.js';
|
||||||
import type TenantContext from '#src/tenants/TenantContext.js';
|
import type TenantContext from '#src/tenants/TenantContext.js';
|
||||||
|
import { conditionalArray } from '#src/utils/array.js';
|
||||||
import { getTenantId } from '#src/utils/tenant.js';
|
import { getTenantId } from '#src/utils/tenant.js';
|
||||||
|
|
||||||
import type { WithInteractionDetailsContext } from '../middleware/koa-interaction-details.js';
|
import type { WithInteractionDetailsContext } from '../middleware/koa-interaction-details.js';
|
||||||
|
@ -166,8 +168,10 @@ export default async function submitInteraction(
|
||||||
|
|
||||||
const { client_id } = ctx.interactionDetails.params;
|
const { client_id } = ctx.interactionDetails.params;
|
||||||
|
|
||||||
|
const { isCloud } = EnvSet.values;
|
||||||
|
const isInAdminTenant = getTenantId(ctx.URL) === adminTenantId;
|
||||||
const isCreatingFirstAdminUser =
|
const isCreatingFirstAdminUser =
|
||||||
getTenantId(ctx.URL) === adminTenantId &&
|
isInAdminTenant &&
|
||||||
String(client_id) === adminConsoleApplicationId &&
|
String(client_id) === adminConsoleApplicationId &&
|
||||||
!(await hasActiveUsers());
|
!(await hasActiveUsers());
|
||||||
|
|
||||||
|
@ -176,13 +180,19 @@ export default async function submitInteraction(
|
||||||
id,
|
id,
|
||||||
...upsertProfile,
|
...upsertProfile,
|
||||||
},
|
},
|
||||||
isCreatingFirstAdminUser ? [UserRole.User, getManagementApiAdminName(defaultTenantId)] : []
|
conditionalArray<string>(
|
||||||
|
isInAdminTenant && UserRole.User,
|
||||||
|
isCreatingFirstAdminUser && getManagementApiAdminName(defaultTenantId),
|
||||||
|
isCreatingFirstAdminUser && isCloud && getManagementApiAdminName(adminTenantId)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// In OSS, we need to limit sign-in experience to "sign-in only" once
|
// In OSS, we need to limit sign-in experience to "sign-in only" once
|
||||||
// the first admin has been create since we don't want other unexpected registrations
|
// the first admin has been create since we don't want other unexpected registrations
|
||||||
if (isCreatingFirstAdminUser) {
|
if (isCreatingFirstAdminUser) {
|
||||||
await updateDefaultSignInExperience({ signInMode: SignInMode.SignIn });
|
await updateDefaultSignInExperience({
|
||||||
|
signInMode: isCloud ? SignInMode.SignInAndRegister : SignInMode.SignIn,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
||||||
|
|
5
packages/core/src/utils/array.ts
Normal file
5
packages/core/src/utils/array.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import type { Falsy } from '@silverhand/essentials';
|
||||||
|
import { notFalsy } from '@silverhand/essentials';
|
||||||
|
|
||||||
|
export const conditionalArray = <T>(...exp: Array<T | Falsy>): T[] =>
|
||||||
|
exp.filter((value): value is Exclude<T, Falsy> => notFalsy(value));
|
19
packages/shared/src/env/GlobalValues.ts
vendored
19
packages/shared/src/env/GlobalValues.ts
vendored
|
@ -57,6 +57,8 @@ export default class GlobalValues {
|
||||||
public readonly isDomainBasedMultiTenancy = this.urlSet.endpoint.hostname.includes('*');
|
public readonly isDomainBasedMultiTenancy = this.urlSet.endpoint.hostname.includes('*');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* **NOTE: This is an internal dev-only feature.**
|
||||||
|
*
|
||||||
* This value indicates path-based multi-tenancy (PBMT) is enabled by setting env variable `PATH_BASED_MULTI_TENANCY` to a truthy value.
|
* This value indicates path-based multi-tenancy (PBMT) is enabled by setting env variable `PATH_BASED_MULTI_TENANCY` to a truthy value.
|
||||||
*
|
*
|
||||||
* Note the value will always be `false` if domain-based multi-tenancy is enabled.
|
* Note the value will always be `false` if domain-based multi-tenancy is enabled.
|
||||||
|
@ -85,6 +87,9 @@ export default class GlobalValues {
|
||||||
return this.isDomainBasedMultiTenancy || this.isPathBasedMultiTenancy;
|
return this.isDomainBasedMultiTenancy || this.isPathBasedMultiTenancy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** If the env explicitly indicates it's in the cloud environment. */
|
||||||
|
public readonly isCloud = yes(getEnv('IS_CLOUD'));
|
||||||
|
|
||||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||||
public readonly databaseUrl = tryThat(() => assertEnv('DB_URL'), throwErrorWithDsnMessage);
|
public readonly databaseUrl = tryThat(() => assertEnv('DB_URL'), throwErrorWithDsnMessage);
|
||||||
public readonly developmentTenantId = getEnv('DEVELOPMENT_TENANT_ID');
|
public readonly developmentTenantId = getEnv('DEVELOPMENT_TENANT_ID');
|
||||||
|
@ -100,4 +105,18 @@ export default class GlobalValues {
|
||||||
public get endpoint(): URL {
|
public get endpoint(): URL {
|
||||||
return this.urlSet.endpoint;
|
return this.urlSet.endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (this.isPathBasedMultiTenancy) {
|
||||||
|
console.warn(
|
||||||
|
'\n****** LOGTO WARNING ******\n\n' +
|
||||||
|
'Path-based multi-tenancy is an internal dev-only feature. It is unstable and not intended for production use.\n\n' +
|
||||||
|
'Known issues:\n\n' +
|
||||||
|
'- Sign-in experience is unavailable in user tenants.\n' +
|
||||||
|
'- The Admin Console may display incorrect user endpoints on multiple pages, such as guide, config, etc.' +
|
||||||
|
' This issue is caused by the native URL constructor new URL(), which overrides the base pathname.\n\n' +
|
||||||
|
'****** END LOGTO WARNING ******\n'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue