0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

refactor(core,connector): store and get fromEmail from logto email connector (#4164)

This commit is contained in:
Darcy Ye 2023-07-14 11:36:24 +08:00 committed by GitHub
parent c5b0f9bdc5
commit 3e08f3c94d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 61 deletions

View file

@ -95,6 +95,11 @@ export const defaultMetadata: ConnectorMetadata = {
type: ConnectorConfigFormItemType.Text,
},
],
/**
* This is the email address that will be used as the sender of the email, should align with
* the `fromEmail` of emailServiceProvider stored in cloud.systems table.
*/
fromEmail: 'no-reply@logto.email',
};
export const scope = ['send:email'];

View file

@ -20,8 +20,6 @@ import { grantAccessToken } from './grant-access-token.js';
import type { LogtoEmailConfig } from './types.js';
import { logtoEmailConfigGuard } from './types.js';
export type { EmailServiceBasicConfig } from './types.js';
const sendMessage =
(getConfig: GetConnectorConfig): SendMessageFunction =>
async (data, inputConfig) => {

View file

@ -1,4 +1,3 @@
/* eslint-disable max-lines */
import { buildRawConnector } from '@logto/cli/lib/connector/index.js';
import { demoConnectorIds, validateConfig } from '@logto/connector-kit';
import {
@ -13,13 +12,12 @@ import { string, object } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import SystemContext from '#src/tenants/SystemContext.js';
import assertThat from '#src/utils/assert-that.js';
import { buildExtraInfo } from '#src/utils/connectors/extra-information.js';
import {
loadConnectorFactories,
transpileConnectorFactory,
transpileLogtoConnector,
buildExtraInfoFromEmailServiceData,
} from '#src/utils/connectors/index.js';
import { checkSocialConnectorTargetAndPlatformUniqueness } from '#src/utils/connectors/platform.js';
@ -46,15 +44,6 @@ export default function connectorRoutes<T extends AuthedRouter>(
signInExperiences: { removeUnavailableSocialConnectorTargets },
} = tenant.libraries;
// Will accept other source of `extraInfo` in the future.
const { emailServiceProviderConfig } = SystemContext.shared;
const buildExtraInfo = (connectorFactoryId: string) => {
const extraInfo = {
...buildExtraInfoFromEmailServiceData(connectorFactoryId, emailServiceProviderConfig),
};
return cleanDeep(extraInfo, { emptyObjects: false });
};
router.post(
'/connectors',
koaGuard({
@ -164,7 +153,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
}
const connector = await getLogtoConnectorById(insertConnectorId);
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata.id));
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata));
return next();
}
@ -200,7 +189,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
ctx.body = await Promise.all(
filteredConnectors.map(async (connector) =>
transpileLogtoConnector(connector, buildExtraInfo(connector.metadata.id))
transpileLogtoConnector(connector, buildExtraInfo(connector.metadata))
)
);
@ -224,7 +213,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
// Hide demo connector
assertThat(!demoConnectorIds.includes(connector.metadata.id), 'connector.not_found');
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata.id));
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata));
return next();
}
@ -324,7 +313,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
jsonbMode: 'replace',
});
const connector = await getLogtoConnectorById(id);
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata.id));
ctx.body = await transpileLogtoConnector(connector, buildExtraInfo(connector.metadata));
return next();
}
@ -360,5 +349,3 @@ export default function connectorRoutes<T extends AuthedRouter>(
connectorConfigTestingRoutes(router, tenant);
connectorAuthorizationUriRoutes(router, tenant);
}
/** TODO @Darcy: refactor this file later. */
/* eslint-enable max-lines */

View file

@ -1,12 +1,9 @@
import {
CloudflareKey,
EmailServiceProviderKey,
type HostnameProviderData,
type StorageProviderData,
type EmailServiceData,
hostnameProviderDataGuard,
storageProviderDataGuard,
emailServiceDataGuard,
StorageProviderKey,
type SystemKey,
} from '@logto/schemas';
@ -20,7 +17,6 @@ export default class SystemContext {
static shared = new SystemContext();
public storageProviderConfig?: StorageProviderData;
public hostnameProviderConfig?: HostnameProviderData;
public emailServiceProviderConfig?: EmailServiceData;
async loadProviderConfigs(pool: CommonQueryMethods) {
await Promise.all([
@ -38,13 +34,6 @@ export default class SystemContext {
hostnameProviderDataGuard
);
})(),
(async () => {
this.emailServiceProviderConfig = await this.loadConfig(
pool,
EmailServiceProviderKey.EmailServiceProvider,
emailServiceDataGuard
);
})(),
]);
}

View file

@ -0,0 +1,18 @@
import { type ConnectorMetadata, ServiceConnector } from '@logto/connector-kit';
import { conditional } from '@silverhand/essentials';
import cleanDeep from 'clean-deep';
import { string, object } from 'zod';
const getFromEmailFromMetadata = (metadata: ConnectorMetadata) => {
const result = object({ fromEmail: string() }).safeParse(metadata);
return conditional(result.success && result.data.fromEmail);
};
// Will accept other source of `extraInfo` in the future.
export const buildExtraInfo = (metadata: ConnectorMetadata) => {
const fromEmail = getFromEmailFromMetadata(metadata);
const extraInfo = {
...conditional(fromEmail && metadata.id === ServiceConnector.Email && { fromEmail }),
};
return cleanDeep(extraInfo, { emptyObjects: false });
};

View file

@ -12,11 +12,10 @@ import {
ConnectorType,
type EmailConnector,
type SmsConnector,
ServiceConnector,
} from '@logto/connector-kit';
import type { ConnectorFactoryResponse, ConnectorResponse, EmailServiceData } from '@logto/schemas';
import type { ConnectorFactoryResponse, ConnectorResponse } from '@logto/schemas';
import { findPackage } from '@logto/shared';
import { conditional, deduplicate, pick, trySafe, type Optional } from '@silverhand/essentials';
import { conditional, deduplicate, pick, trySafe } from '@silverhand/essentials';
import { EnvSet } from '#src/env-set/index.js';
import RequestError from '#src/errors/RequestError/index.js';
@ -76,20 +75,6 @@ export const transpileConnectorFactory = ({
};
};
/**
* `extraInfo` is only used to expose email service vendors `fromEmail` setup to Logto email connector.
* Can extend this method in the future for other use cases.
*/
export const buildExtraInfoFromEmailServiceData = (
connectorFactoryId: string,
emailServiceProviderConfig?: EmailServiceData
): Optional<Record<string, unknown>> => {
return conditional(
connectorFactoryId === ServiceConnector.Email &&
emailServiceProviderConfig?.fromEmail && { fromEmail: emailServiceProviderConfig.fromEmail }
);
};
const checkDuplicateConnectorFactoriesId = (connectorFactories: ConnectorFactory[]) => {
const connectorFactoryIds = connectorFactories.map(({ metadata }) => metadata.id);
const deduplicatedConnectorFactoryIds = deduplicate(connectorFactoryIds);

View file

@ -138,7 +138,8 @@ const connectorConfigFormItemGuard = z.discriminatedUnion('type', [
export type ConnectorConfigFormItem = z.infer<typeof connectorConfigFormItemGuard>;
export const connectorMetadataGuard = z.object({
export const connectorMetadataGuard = z
.object({
id: z.string(),
target: z.string(),
platform: z.nativeEnum(ConnectorPlatform).nullable(),
@ -150,7 +151,8 @@ export const connectorMetadataGuard = z.object({
readme: z.string(),
configTemplate: z.string().optional(),
formItems: connectorConfigFormItemGuard.array().optional(),
});
})
.catchall(z.unknown());
export const configurableConnectorMetadataGuard = connectorMetadataGuard
.pick({