From baaa58ca0b0dc6ec8c9e1341746aea2b59679f31 Mon Sep 17 00:00:00 2001
From: simeng-li <simeng@silverhand.io>
Date: Fri, 23 Dec 2022 19:09:09 +0800
Subject: [PATCH] refactor(core): per review

add the missing file

refactor: remove unused code

fix(core): rename interactionSie middleware

rename interactionSie middleware

fix(core): update context type name

update context type name

refactor: per review
---
 packages/core/src/env-set/index.ts            |  3 ++
 packages/core/src/index.ts                    | 26 +++++++++------
 packages/core/src/libraries/hook.ts           | 13 +-------
 packages/core/src/routes/interaction/index.ts | 18 +++++------
 .../middleware/koa-interaction-sie.ts         | 32 +++++++++++++++++++
 .../mandatory-user-profile-validation.ts      |  4 +--
 packages/schemas/src/models/hooks.ts          |  5 +--
 7 files changed, 65 insertions(+), 36 deletions(-)
 create mode 100644 packages/core/src/routes/interaction/middleware/koa-interaction-sie.ts

diff --git a/packages/core/src/env-set/index.ts b/packages/core/src/env-set/index.ts
index e50b6f42a..5f1a775d1 100644
--- a/packages/core/src/env-set/index.ts
+++ b/packages/core/src/env-set/index.ts
@@ -87,6 +87,9 @@ function createEnvSet() {
 
       return queryClient;
     },
+    get queryClientSafe() {
+      return queryClient;
+    },
     get oidc() {
       if (!oidc) {
         return throwNotLoadedError();
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 91d0e2924..428f52bf4 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,16 +1,24 @@
+import { noop } from '@silverhand/essentials';
 import Koa from 'koa';
 
 import { configDotEnv } from './env-set/dot-env.js';
 import envSet from './env-set/index.js';
 import initI18n from './i18n/init.js';
 
-await configDotEnv();
-await envSet.load();
-const app = new Koa({
-  proxy: envSet.values.trustProxyHeader,
-});
-await initI18n();
+try {
+  await configDotEnv();
+  await envSet.load();
+  const app = new Koa({
+    proxy: envSet.values.trustProxyHeader,
+  });
+  await initI18n();
 
-// Import last until init completed
-const { default: initApp } = await import('./app/init.js');
-await initApp(app);
+  // Import last until init completed
+  const { default: initApp } = await import('./app/init.js');
+  await initApp(app);
+} catch (error: unknown) {
+  console.error('Error while initializing app:');
+  console.error(error);
+
+  await Promise.all([envSet.poolSafe?.end(), envSet.queryClientSafe?.end()]).catch(noop);
+}
diff --git a/packages/core/src/libraries/hook.ts b/packages/core/src/libraries/hook.ts
index 6f1b5fd7b..3f6ab99c3 100644
--- a/packages/core/src/libraries/hook.ts
+++ b/packages/core/src/libraries/hook.ts
@@ -1,7 +1,7 @@
 import { Event, userInfoSelectFields } from '@logto/schemas';
 import { HookEventPayload, HookEvent } from '@logto/schemas/models';
 import { trySafe } from '@logto/shared';
-import { conditional } from '@silverhand/essentials';
+import { conditional, pick } from '@silverhand/essentials';
 import { got } from 'got';
 import type { Provider } from 'oidc-provider';
 
@@ -16,17 +16,6 @@ const eventToHook: Record<Event, HookEvent> = {
   [Event.ForgotPassword]: HookEvent.PostResetPassword,
 };
 
-// TODO: replace `lodash.pick`
-const pick = <T, Keys extends Array<keyof T>>(
-  object: T,
-  ...keys: Keys
-): { [key in Keys[number]]: T[key] } => {
-  // eslint-disable-next-line no-restricted-syntax
-  return Object.fromEntries(keys.map((key) => [key, object[key]])) as {
-    [key in Keys[number]]: T[key];
-  };
-};
-
 export type Interaction = Awaited<ReturnType<Provider['interactionDetails']>>;
 
 export const triggerInteractionHooksIfNeeded = async (
diff --git a/packages/core/src/routes/interaction/index.ts b/packages/core/src/routes/interaction/index.ts
index 92170e8b9..5eb8e51aa 100644
--- a/packages/core/src/routes/interaction/index.ts
+++ b/packages/core/src/routes/interaction/index.ts
@@ -15,7 +15,7 @@ import submitInteraction from './actions/submit-interaction.js';
 import koaInteractionDetails from './middleware/koa-interaction-details.js';
 import type { WithInteractionDetailsContext } from './middleware/koa-interaction-details.js';
 import koaInteractionHooks from './middleware/koa-interaction-hooks.js';
-import koaInteractionSIE from './middleware/koa-interaction-sie.js';
+import koaInteractionSie from './middleware/koa-interaction-sie.js';
 import { sendPasscodePayloadGuard, socialAuthorizationUrlPayloadGuard } from './types/guard.js';
 import {
   getInteractionStorage,
@@ -63,7 +63,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
         profile: profileGuard.optional(),
       }),
     }),
-    koaInteractionSIE(),
+    koaInteractionSie(),
     async (ctx, next) => {
       const { event, identifier, profile } = ctx.guard.body;
       const { signInExperience } = ctx;
@@ -108,7 +108,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
   router.put(
     `${interactionPrefix}/event`,
     koaGuard({ body: z.object({ event: eventGuard }) }),
-    koaInteractionSIE(),
+    koaInteractionSie(),
     async (ctx, next) => {
       const { event } = ctx.guard.body;
       const { signInExperience, interactionDetails } = ctx;
@@ -141,7 +141,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
     koaGuard({
       body: identifierPayloadGuard,
     }),
-    koaInteractionSIE(),
+    koaInteractionSie(),
     async (ctx, next) => {
       const identifierPayload = ctx.guard.body;
       const { signInExperience, interactionDetails } = ctx;
@@ -172,7 +172,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
     koaGuard({
       body: profileGuard,
     }),
-    koaInteractionSIE(),
+    koaInteractionSie(),
     async (ctx, next) => {
       const profilePayload = ctx.guard.body;
       const { signInExperience, interactionDetails } = ctx;
@@ -213,7 +213,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
   // Submit Interaction
   router.post(
     `${interactionPrefix}/submit`,
-    koaInteractionSIE(),
+    koaInteractionSie(),
     koaInteractionHooks(),
     async (ctx, next) => {
       const { interactionDetails } = ctx;
@@ -260,11 +260,11 @@ export default function interactionRoutes<T extends AnonymousRouter>(
       body: sendPasscodePayloadGuard,
     }),
     async (ctx, next) => {
+      const { interactionDetails, guard, createLog } = ctx;
       // Check interaction exists
-      getInteractionStorage(ctx.interactionDetails.result);
+      getInteractionStorage(interactionDetails.result);
 
-      const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
-      await sendPasscodeToIdentifier(ctx.guard.body, jti, ctx.createLog);
+      await sendPasscodeToIdentifier(guard.body, interactionDetails.jti, createLog);
 
       ctx.status = 204;
 
diff --git a/packages/core/src/routes/interaction/middleware/koa-interaction-sie.ts b/packages/core/src/routes/interaction/middleware/koa-interaction-sie.ts
new file mode 100644
index 000000000..8d88e7a83
--- /dev/null
+++ b/packages/core/src/routes/interaction/middleware/koa-interaction-sie.ts
@@ -0,0 +1,32 @@
+import type { SignInExperience } from '@logto/schemas';
+import { conditional } from '@silverhand/essentials';
+import type { MiddlewareType } from 'koa';
+
+import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
+
+import type { WithInteractionDetailsContext } from './koa-interaction-details.js';
+
+export type WithInteractionSieContext<ContextT> = WithInteractionDetailsContext<ContextT> & {
+  signInExperience: SignInExperience;
+};
+
+export default function koaInteractionSie<StateT, ContextT, ResponseT>(): MiddlewareType<
+  StateT,
+  WithInteractionSieContext<ContextT>,
+  ResponseT
+> {
+  return async (ctx, next) => {
+    const { interactionDetails } = ctx;
+
+    const signInExperience = await getSignInExperienceForApplication(
+      conditional(
+        typeof interactionDetails.params.client_id === 'string' &&
+          interactionDetails.params.client_id
+      )
+    );
+
+    ctx.signInExperience = signInExperience;
+
+    return next();
+  };
+}
diff --git a/packages/core/src/routes/interaction/verifications/mandatory-user-profile-validation.ts b/packages/core/src/routes/interaction/verifications/mandatory-user-profile-validation.ts
index b5db2786d..4edf54c54 100644
--- a/packages/core/src/routes/interaction/verifications/mandatory-user-profile-validation.ts
+++ b/packages/core/src/routes/interaction/verifications/mandatory-user-profile-validation.ts
@@ -7,7 +7,7 @@ import RequestError from '#src/errors/RequestError/index.js';
 import { findUserById } from '#src/queries/user.js';
 import assertThat from '#src/utils/assert-that.js';
 
-import type { WithInteractionSIEContext } from '../middleware/koa-interaction-sie.js';
+import type { WithInteractionSieContext } from '../middleware/koa-interaction-sie.js';
 import type { IdentifierVerifiedInteractionResult } from '../types/index.js';
 import { isUserPasswordSet } from '../utils/index.js';
 
@@ -70,7 +70,7 @@ const getMissingProfileBySignUpIdentifiers = ({
 };
 
 export default async function validateMandatoryUserProfile(
-  ctx: WithInteractionSIEContext<Context>,
+  ctx: WithInteractionSieContext<Context>,
   interaction: IdentifierVerifiedInteractionResult
 ) {
   const { signUp } = ctx.signInExperience;
diff --git a/packages/schemas/src/models/hooks.ts b/packages/schemas/src/models/hooks.ts
index 595b8c8b9..e74cc775d 100644
--- a/packages/schemas/src/models/hooks.ts
+++ b/packages/schemas/src/models/hooks.ts
@@ -2,7 +2,7 @@ import { generateStandardId } from '@logto/core-kit';
 import { createModel } from '@withtyped/server';
 import { z } from 'zod';
 
-import type { Application, Connector, User } from '../db-entries/index.js';
+import type { Application, User } from '../db-entries/index.js';
 import type { userInfoSelectFields } from '../types/index.js';
 
 export enum HookEvent {
@@ -20,9 +20,6 @@ export type HookEventPayload = {
   userId?: string;
   user?: Pick<User, typeof userInfoSelectFields[number]>;
   application?: Pick<Application, 'id' | 'type' | 'name' | 'description'>;
-  connectors?: Array<
-    Pick<Connector, 'id'> & Pick<Connector['metadata'], 'name'> & Record<string, unknown>
-  >;
 } & Record<string, unknown>;
 
 export type HookConfig = {