From f6d38163bbd1572db493fec9392b3475e880aa67 Mon Sep 17 00:00:00 2001 From: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:29:15 +0800 Subject: [PATCH] Feat(medusa, user, core-flows): User creation with invites (#6413) **What** - add accept invite endpoint **Invite accept flow** - authenticate using the auth endpoint to create and auth-user - invoke accept endpoint with token and info to create user --- .../__tests__/invites/create-invite.spec.ts | 3 +- .../__tests__/invites/delete-invite.spec.ts | 3 +- .../__tests__/invites/list-invites.spec.ts | 3 +- .../__tests__/invites/retrieve-invite.spec.ts | 8 +- .../plugins/helpers/create-admin-user.ts | 25 ++++ integration-tests/plugins/medusa-config.js | 3 + .../src/invite/steps/validate-token.ts | 19 +++ .../src/invite/workflows/accept-invite.ts | 52 ++++++++ .../core-flows/src/invite/workflows/index.ts | 1 + .../src/api-v2/admin/invites/accept/route.ts | 34 +++++ .../src/api-v2/admin/invites/middlewares.ts | 16 +++ .../src/api-v2/admin/invites/validators.ts | 68 +++++++++- packages/types/src/user/mutations.ts | 3 +- packages/types/src/user/service.ts | 5 + .../src/workflow/invite/accept-invite.ts | 10 ++ packages/types/src/workflow/invite/index.ts | 2 +- .../src/workflow/invite/update-invite.ts | 5 - .../utils/get-init-module-config.ts | 1 + packages/user/package.json | 1 + packages/user/src/services/index.ts | 1 + packages/user/src/services/invite.ts | 126 ++++++++++++++++++ packages/user/src/services/user-module.ts | 25 ++-- packages/user/src/types/index.ts | 2 +- packages/user/src/types/services/index.ts | 2 +- packages/user/src/types/services/invite.ts | 5 + packages/user/src/types/services/user.ts | 13 -- .../utils/src/auth/abstract-auth-provider.ts | 2 +- yarn.lock | 1 + 28 files changed, 399 insertions(+), 40 deletions(-) create mode 100644 integration-tests/plugins/helpers/create-admin-user.ts create mode 100644 packages/core-flows/src/invite/steps/validate-token.ts create mode 100644 packages/core-flows/src/invite/workflows/accept-invite.ts create mode 100644 packages/medusa/src/api-v2/admin/invites/accept/route.ts create mode 100644 packages/types/src/workflow/invite/accept-invite.ts delete mode 100644 packages/types/src/workflow/invite/update-invite.ts create mode 100644 packages/user/src/services/invite.ts create mode 100644 packages/user/src/types/services/invite.ts delete mode 100644 packages/user/src/types/services/user.ts diff --git a/integration-tests/plugins/__tests__/invites/create-invite.spec.ts b/integration-tests/plugins/__tests__/invites/create-invite.spec.ts index fac5a92d1c..5d36823db9 100644 --- a/integration-tests/plugins/__tests__/invites/create-invite.spec.ts +++ b/integration-tests/plugins/__tests__/invites/create-invite.spec.ts @@ -5,6 +5,7 @@ import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app" import { useApi } from "../../../environment-helpers/use-api" import adminSeeder from "../../../helpers/admin-seeder" import { AxiosInstance } from "axios" +import { createAdminUser } from "../../helpers/create-admin-user" jest.setTimeout(50000) @@ -24,7 +25,7 @@ describe("POST /admin/invites", () => { }) beforeEach(async () => { - await adminSeeder(dbConnection) + await createAdminUser(dbConnection, adminHeaders) }) afterAll(async () => { diff --git a/integration-tests/plugins/__tests__/invites/delete-invite.spec.ts b/integration-tests/plugins/__tests__/invites/delete-invite.spec.ts index 207619a61f..cfb7b069f0 100644 --- a/integration-tests/plugins/__tests__/invites/delete-invite.spec.ts +++ b/integration-tests/plugins/__tests__/invites/delete-invite.spec.ts @@ -8,6 +8,7 @@ import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app" import { useApi } from "../../../environment-helpers/use-api" import adminSeeder from "../../../helpers/admin-seeder" import { AxiosInstance } from "axios" +import { createAdminUser } from "../../helpers/create-admin-user" jest.setTimeout(50000) @@ -31,7 +32,7 @@ describe("DELETE /admin/invites/:id", () => { }) beforeEach(async () => { - await adminSeeder(dbConnection) + await createAdminUser(dbConnection, adminHeaders) }) afterAll(async () => { diff --git a/integration-tests/plugins/__tests__/invites/list-invites.spec.ts b/integration-tests/plugins/__tests__/invites/list-invites.spec.ts index ab1165fadd..b172af9c5a 100644 --- a/integration-tests/plugins/__tests__/invites/list-invites.spec.ts +++ b/integration-tests/plugins/__tests__/invites/list-invites.spec.ts @@ -8,6 +8,7 @@ import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app" import { useApi } from "../../../environment-helpers/use-api" import adminSeeder from "../../../helpers/admin-seeder" import { AxiosInstance } from "axios" +import { createAdminUser } from "../../helpers/create-admin-user" jest.setTimeout(50000) @@ -31,7 +32,7 @@ describe("GET /admin/invites", () => { }) beforeEach(async () => { - await adminSeeder(dbConnection) + await createAdminUser(dbConnection, adminHeaders) }) afterAll(async () => { diff --git a/integration-tests/plugins/__tests__/invites/retrieve-invite.spec.ts b/integration-tests/plugins/__tests__/invites/retrieve-invite.spec.ts index 2f283c8b76..f81897f33e 100644 --- a/integration-tests/plugins/__tests__/invites/retrieve-invite.spec.ts +++ b/integration-tests/plugins/__tests__/invites/retrieve-invite.spec.ts @@ -1,13 +1,13 @@ import { initDb, useDb } from "../../../environment-helpers/use-db" -import { IUserModuleService } from "@medusajs/types" +import { IAuthModuleService, IUserModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { getContainer } from "../../../environment-helpers/use-container" import path from "path" import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app" import { useApi } from "../../../environment-helpers/use-api" -import adminSeeder from "../../../helpers/admin-seeder" import { AxiosInstance } from "axios" +import { createAdminUser } from "../../helpers/create-admin-user" jest.setTimeout(50000) @@ -31,7 +31,7 @@ describe("GET /admin/invites/:id", () => { }) beforeEach(async () => { - await adminSeeder(dbConnection) + await createAdminUser(dbConnection, adminHeaders) }) afterAll(async () => { @@ -48,8 +48,6 @@ describe("GET /admin/invites/:id", () => { it("should retrieve a single invite", async () => { const invite = await userModuleService.createInvites({ email: "potential_member@test.com", - token: "test", - expires_at: new Date(), }) const api = useApi()! as AxiosInstance diff --git a/integration-tests/plugins/helpers/create-admin-user.ts b/integration-tests/plugins/helpers/create-admin-user.ts new file mode 100644 index 0000000000..118439fe80 --- /dev/null +++ b/integration-tests/plugins/helpers/create-admin-user.ts @@ -0,0 +1,25 @@ +import { IAuthModuleService } from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import adminSeeder from "../../helpers/admin-seeder" +import jwt from "jsonwebtoken" +import { getContainer } from "../../environment-helpers/use-container" + +export const createAdminUser = async (dbConnection, adminHeaders) => { + await adminSeeder(dbConnection) + const appContainer = getContainer()! + + const authModule: IAuthModuleService = appContainer.resolve( + ModuleRegistrationName.AUTH + ) + const authUser = await authModule.create({ + provider: "emailpass", + entity_id: "admin@medusa.js", + scope: "admin", + app_metadata: { + user_id: "admin_user", + }, + }) + + const token = jwt.sign(authUser, "test") + adminHeaders.headers["authorization"] = `Bearer ${token}` +} diff --git a/integration-tests/plugins/medusa-config.js b/integration-tests/plugins/medusa-config.js index 4c3b7ab81e..b81d6e9d1c 100644 --- a/integration-tests/plugins/medusa-config.js +++ b/integration-tests/plugins/medusa-config.js @@ -48,6 +48,9 @@ module.exports = { scope: "internal", resources: "shared", resolve: "@medusajs/user", + options: { + jwt_secret: "test", + }, }, [Modules.STOCK_LOCATION]: { scope: "internal", diff --git a/packages/core-flows/src/invite/steps/validate-token.ts b/packages/core-flows/src/invite/steps/validate-token.ts new file mode 100644 index 0000000000..849d32fad5 --- /dev/null +++ b/packages/core-flows/src/invite/steps/validate-token.ts @@ -0,0 +1,19 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { InviteDTO } from "@medusajs/types" +import { IUserModuleService } from "@medusajs/types" +import { StepResponse } from "@medusajs/workflows-sdk" +import { createStep } from "@medusajs/workflows-sdk" + +export const validateTokenStepId = "validate-invite-token-step" +export const validateTokenStep = createStep( + validateTokenStepId, + async (input: string, { container }) => { + const userModuleService: IUserModuleService = container.resolve( + ModuleRegistrationName.USER + ) + + const invite = await userModuleService.validateInviteToken(input) + + return new StepResponse(invite) + } +) diff --git a/packages/core-flows/src/invite/workflows/accept-invite.ts b/packages/core-flows/src/invite/workflows/accept-invite.ts new file mode 100644 index 0000000000..485eaa8c49 --- /dev/null +++ b/packages/core-flows/src/invite/workflows/accept-invite.ts @@ -0,0 +1,52 @@ +import { UserDTO } from "@medusajs/types" +import { + WorkflowData, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { createUsersStep } from "../../user" +import { validateTokenStep } from "../steps/validate-token" +import { setAuthAppMetadataStep } from "../../auth" +import { InviteWorkflow } from "@medusajs/types" +import { deleteInvitesStep } from "../steps" + +export const acceptInviteWorkflowId = "accept-invite-workflow" +export const acceptInviteWorkflow = createWorkflow( + acceptInviteWorkflowId, + ( + input: WorkflowData + ): WorkflowData => { + // validate token + const invite = validateTokenStep(input.invite_token) + + const createUserInput = transform( + { input, invite }, + ({ input, invite }) => { + return [ + { + ...input.user, + email: invite.email, + }, + ] + } + ) + + const users = createUsersStep(createUserInput) + + const authUserInput = transform({ input, users }, ({ input, users }) => { + const createdUser = users[0] + + return { + authUserId: input.auth_user_id, + key: "user_id", + value: createdUser.id, + } + }) + + setAuthAppMetadataStep(authUserInput) + + deleteInvitesStep([invite.id]) + + return users + } +) diff --git a/packages/core-flows/src/invite/workflows/index.ts b/packages/core-flows/src/invite/workflows/index.ts index 1f17f0e332..db6489b250 100644 --- a/packages/core-flows/src/invite/workflows/index.ts +++ b/packages/core-flows/src/invite/workflows/index.ts @@ -1,2 +1,3 @@ export * from "./create-invites" export * from "./delete-invites" +export * from "./accept-invite" diff --git a/packages/medusa/src/api-v2/admin/invites/accept/route.ts b/packages/medusa/src/api-v2/admin/invites/accept/route.ts new file mode 100644 index 0000000000..c9d09bb1ec --- /dev/null +++ b/packages/medusa/src/api-v2/admin/invites/accept/route.ts @@ -0,0 +1,34 @@ +import { acceptInviteWorkflow } from "@medusajs/core-flows" +import { MedusaRequest, MedusaResponse } from "../../../../types/routing" +import { InviteWorkflow } from "@medusajs/types" +import { AdminPostInvitesInviteAcceptReq } from "../validators" +import { IUserModuleService } from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" + +export const POST = async (req: MedusaRequest, res: MedusaResponse) => { + if (req.auth_user?.app_metadata?.user_id) { + const moduleService: IUserModuleService = req.scope.resolve( + ModuleRegistrationName.USER + ) + const user = moduleService.retrieve(req.auth_user.app_metadata.user_id) + res.status(200).json({ user }) + return + } + + const workflow = acceptInviteWorkflow(req.scope) + + const input = { + invite_token: req.filterableFields.token as string, + auth_user_id: req.auth_user!.id, + user: req.validatedBody as AdminPostInvitesInviteAcceptReq, + } as InviteWorkflow.AcceptInviteWorkflowInputDTO + + const { result: users } = await workflow.run({ input }) + + // Set customer_id on session user if we are in session + if (req.session.auth_user) { + req.session.auth_user.app_metadata.user_id = users[0].id + } + + res.status(200).json({ user: users[0] }) +} diff --git a/packages/medusa/src/api-v2/admin/invites/middlewares.ts b/packages/medusa/src/api-v2/admin/invites/middlewares.ts index 5381ce553a..91f3152e55 100644 --- a/packages/medusa/src/api-v2/admin/invites/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/invites/middlewares.ts @@ -3,11 +3,19 @@ import { AdminCreateInviteRequest, AdminGetInvitesParams, AdminGetInvitesInviteParams, + AdminPostInvitesInviteAcceptReq, + AdminPostInvitesInviteAcceptParams, } from "./validators" import * as QueryConfig from "./query-config" import { MiddlewareRoute } from "../../../types/middlewares" +import { authenticate } from "../../../utils/authenticate-middleware" export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [ + { + method: "ALL", + matcher: "/admin/invites*", + middlewares: [authenticate("admin", ["session", "bearer"])], + }, { method: ["GET"], matcher: "/admin/invites", @@ -23,6 +31,14 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [ matcher: "/admin/invites", middlewares: [transformBody(AdminCreateInviteRequest)], }, + { + method: "POST", + matcher: "/admin/invites/accept", + middlewares: [ + transformBody(AdminPostInvitesInviteAcceptReq), + transformQuery(AdminPostInvitesInviteAcceptParams), + ], + }, { method: ["GET"], matcher: "/admin/invites/:id", diff --git a/packages/medusa/src/api-v2/admin/invites/validators.ts b/packages/medusa/src/api-v2/admin/invites/validators.ts index 3b43bf7274..ddeae230ec 100644 --- a/packages/medusa/src/api-v2/admin/invites/validators.ts +++ b/packages/medusa/src/api-v2/admin/invites/validators.ts @@ -1,5 +1,11 @@ import { Type } from "class-transformer" -import { IsEmail, IsOptional, IsString, ValidateNested } from "class-validator" +import { + IsEmail, + IsNotEmpty, + IsOptional, + IsString, + ValidateNested, +} from "class-validator" import { DateComparisonOperator, FindParams, @@ -70,3 +76,63 @@ export class AdminCreateInviteRequest { @IsEmail() email: string } + +/** + * Details of the use accepting the invite. + */ +export class AdminPostInvitesInviteAcceptUserReq {} + +/** + * @schema AdminPostInvitesInviteAcceptReq + * type: object + * description: "The details of the invite to be accepted." + * required: + * - token + * - user + * properties: + * token: + * description: "The token of the invite to accept. This is a unique token generated when the invite was created or resent." + * type: string + * user: + * description: "The details of the user to create." + * type: object + * required: + * - first_name + * - last_name + * - password + * properties: + * first_name: + * type: string + * description: the first name of the User + * last_name: + * type: string + * description: the last name of the User + * password: + * description: The password for the User + * type: string + * format: password + */ +export class AdminPostInvitesInviteAcceptReq { + /** + * The invite's first name. + */ + @IsString() + @IsOptional() + first_name: string + + /** + * The invite's last name. + */ + @IsString() + @IsOptional() + last_name: string +} + +export class AdminPostInvitesInviteAcceptParams { + @IsString() + @IsNotEmpty() + token: string + + @IsOptional() + expand = undefined +} diff --git a/packages/types/src/user/mutations.ts b/packages/types/src/user/mutations.ts index c27d224196..2baae936dc 100644 --- a/packages/types/src/user/mutations.ts +++ b/packages/types/src/user/mutations.ts @@ -15,6 +15,7 @@ export interface CreateInviteDTO { metadata?: Record | null } -export interface UpdateInviteDTO extends Partial { +export interface UpdateInviteDTO + extends Partial> { id: string } diff --git a/packages/types/src/user/service.ts b/packages/types/src/user/service.ts index eecfe21a7e..3f1e60db9b 100644 --- a/packages/types/src/user/service.ts +++ b/packages/types/src/user/service.ts @@ -12,6 +12,11 @@ import { IModuleService } from "../modules-sdk" import { RestoreReturn, SoftDeleteReturn } from "../dal" export interface IUserModuleService extends IModuleService { + validateInviteToken( + token: string, + sharedContext?: Context + ): Promise + retrieve( id: string, config?: FindConfig, diff --git a/packages/types/src/workflow/invite/accept-invite.ts b/packages/types/src/workflow/invite/accept-invite.ts new file mode 100644 index 0000000000..fdda032c4f --- /dev/null +++ b/packages/types/src/workflow/invite/accept-invite.ts @@ -0,0 +1,10 @@ +export interface AcceptInviteWorkflowInputDTO { + invite_token: string + auth_user_id: string + user: { + first_name?: string | null + last_name?: string | null + avatar_url?: string | null + metadata?: Record | null + } +} diff --git a/packages/types/src/workflow/invite/index.ts b/packages/types/src/workflow/invite/index.ts index 119d7e6ccd..b4703692ee 100644 --- a/packages/types/src/workflow/invite/index.ts +++ b/packages/types/src/workflow/invite/index.ts @@ -1,3 +1,3 @@ export * from "./create-invite" -export * from "./update-invite" export * from "./delete-invite" +export * from "./accept-invite" diff --git a/packages/types/src/workflow/invite/update-invite.ts b/packages/types/src/workflow/invite/update-invite.ts deleted file mode 100644 index 1af1a8e5ca..0000000000 --- a/packages/types/src/workflow/invite/update-invite.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { UpdateInviteDTO } from "../../user" - -export interface UpdateInvitesWorkflowInputDTO { - updates: UpdateInviteDTO[] -} diff --git a/packages/user/integration-tests/utils/get-init-module-config.ts b/packages/user/integration-tests/utils/get-init-module-config.ts index 1150a17fa8..dc92e2fe83 100644 --- a/packages/user/integration-tests/utils/get-init-module-config.ts +++ b/packages/user/integration-tests/utils/get-init-module-config.ts @@ -10,6 +10,7 @@ export function getInitModuleConfig() { schema: process.env.MEDUSA_USER_DB_SCHEMA, }, }, + jwt_secret: "test", } const injectedDependencies = {} diff --git a/packages/user/package.json b/packages/user/package.json index ef3958eb83..8067caddaa 100644 --- a/packages/user/package.json +++ b/packages/user/package.json @@ -56,6 +56,7 @@ "@mikro-orm/postgresql": "5.9.7", "awilix": "^8.0.0", "dotenv": "16.3.1", + "jsonwebtoken": "^9.0.2", "knex": "2.4.2" } } diff --git a/packages/user/src/services/index.ts b/packages/user/src/services/index.ts index fa745e078b..6761aa1bb0 100644 --- a/packages/user/src/services/index.ts +++ b/packages/user/src/services/index.ts @@ -1 +1,2 @@ export { default as UserModuleService } from "./user-module" +export { default as InviteService } from "./invite" diff --git a/packages/user/src/services/invite.ts b/packages/user/src/services/invite.ts new file mode 100644 index 0000000000..ac68003c4c --- /dev/null +++ b/packages/user/src/services/invite.ts @@ -0,0 +1,126 @@ +import { Context, DAL } from "@medusajs/types" +import { + InjectTransactionManager, + MedusaError, + ModulesSdkUtils, +} from "@medusajs/utils" +import { Invite } from "@models" +import { InviteServiceTypes } from "@types" +import jwt, { JwtPayload } from "jsonwebtoken" + +type InjectedDependencies = { + inviteRepository: DAL.RepositoryService +} + +// 7 days +const DEFAULT_VALID_INVITE_DURATION = 1000 * 60 * 60 * 24 * 7 + +export default class InviteService< + TEntity extends Invite = Invite +> extends ModulesSdkUtils.internalModuleServiceFactory( + Invite +) { + // eslint-disable-next-line max-len + protected readonly inviteRepository_: DAL.RepositoryService + protected options_: { jwt_secret: string; valid_duration: number } | undefined + + constructor(container: InjectedDependencies) { + super(container) + this.inviteRepository_ = container.inviteRepository + } + + public withModuleOptions(options: any) { + const service = new InviteService(this.__container__) + + service.options_ = options + + return service + } + + private getOption(key: string) { + if (!this.options_) { + throw new MedusaError( + MedusaError.Types.UNEXPECTED_STATE, + `Options are not configured for InviteService, call "withModuleOptions" and provide options` + ) + } + return this.options_[key] + } + + create( + data: InviteServiceTypes.CreateInviteDTO, + context?: Context + ): Promise + create( + data: InviteServiceTypes.CreateInviteDTO[], + context?: Context + ): Promise + + @InjectTransactionManager("inviteRepository_") + async create( + data: + | InviteServiceTypes.CreateInviteDTO + | InviteServiceTypes.CreateInviteDTO[], + context: Context = {} + ): Promise { + const data_ = Array.isArray(data) ? data : [data] + + const invites = await super.create(data_, context) + + const expiresIn: number = + parseInt(this.getOption("valid_duration")) || + DEFAULT_VALID_INVITE_DURATION + + const updates = invites.map((invite) => { + return { + id: invite.id, + expires_at: new Date().setMilliseconds( + new Date().getMilliseconds() + expiresIn + ), + token: this.generateToken({ id: invite.id }), + } + }) + + return await super.update(updates, context) + } + + @InjectTransactionManager("inviteRepository_") + async validateInviteToken( + token: string, + context?: Context + ): Promise { + const decoded = this.validateToken(token) + + return await super.retrieve(decoded.payload.id, {}, context) + } + + private generateToken(data: any): string { + const jwtSecret: string = this.getOption("jwt_secret") + const expiresIn: number = + parseInt(this.getOption("valid_duration")) || + DEFAULT_VALID_INVITE_DURATION + + if (!jwtSecret) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "No jwt_secret was provided in the UserModule's options. Please add one." + ) + } + + return jwt.sign(data, jwtSecret, { + expiresIn, + }) + } + private validateToken(data: any): JwtPayload { + const jwtSecret = this.getOption("jwt_secret") + + if (!jwtSecret) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "No jwt_secret was provided in the UserModule's options. Please add one." + ) + } + + return jwt.verify(data, jwtSecret, { complete: true }) + } +} diff --git a/packages/user/src/services/user-module.ts b/packages/user/src/services/user-module.ts index d01cac59a4..d7ce630419 100644 --- a/packages/user/src/services/user-module.ts +++ b/packages/user/src/services/user-module.ts @@ -10,16 +10,18 @@ import { InjectManager, InjectTransactionManager, MedusaContext, + MedusaError, ModulesSdkUtils, } from "@medusajs/utils" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { Invite, User } from "@models" +import InviteService from "./invite" type InjectedDependencies = { baseRepository: DAL.RepositoryService userService: ModulesSdkTypes.InternalModuleService - inviteService: ModulesSdkTypes.InternalModuleService + inviteService: InviteService } const generateMethodForModels = [Invite] @@ -46,7 +48,7 @@ export default class UserModuleService< protected baseRepository_: DAL.RepositoryService protected readonly userService_: ModulesSdkTypes.InternalModuleService - protected readonly inviteService_: ModulesSdkTypes.InternalModuleService + protected readonly inviteService_: InviteService constructor( { userService, inviteService, baseRepository }: InjectedDependencies, @@ -57,7 +59,17 @@ export default class UserModuleService< this.baseRepository_ = baseRepository this.userService_ = userService - this.inviteService_ = inviteService + this.inviteService_ = inviteService.withModuleOptions( + this.moduleDeclaration + ) + } + + @InjectTransactionManager("baseRepository_") + async validateInviteToken( + token: string, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return await this.inviteService_.validateInviteToken(token, sharedContext) } create( @@ -146,14 +158,11 @@ export default class UserModuleService< data: UserTypes.CreateInviteDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { - // expiration date in 10 days - const expirationDate = new Date().setDate(new Date().getDate() + 10) - const toCreate = data.map((invite) => { return { ...invite, - expires_at: new Date(expirationDate), - token: "placeholder", // TODO: generate token + expires_at: new Date(), + token: "placeholder", } }) diff --git a/packages/user/src/types/index.ts b/packages/user/src/types/index.ts index 48641f932f..591aa4b9a5 100644 --- a/packages/user/src/types/index.ts +++ b/packages/user/src/types/index.ts @@ -4,4 +4,4 @@ export type InitializeModuleInjectableDependencies = { logger?: Logger } -export * as ServiceTypes from "./services" +export * from "./services" diff --git a/packages/user/src/types/services/index.ts b/packages/user/src/types/services/index.ts index 50c6bf22c6..841ed18c8d 100644 --- a/packages/user/src/types/services/index.ts +++ b/packages/user/src/types/services/index.ts @@ -1 +1 @@ -export * from "./user" +export * as InviteServiceTypes from "./invite" diff --git a/packages/user/src/types/services/invite.ts b/packages/user/src/types/services/invite.ts new file mode 100644 index 0000000000..4f5bdf77ca --- /dev/null +++ b/packages/user/src/types/services/invite.ts @@ -0,0 +1,5 @@ +export interface CreateInviteDTO { + email: string + accepted?: boolean + metadata?: Record | null +} diff --git a/packages/user/src/types/services/user.ts b/packages/user/src/types/services/user.ts deleted file mode 100644 index 63ad82acc9..0000000000 --- a/packages/user/src/types/services/user.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type UserDTO = { - id: string -} - -export type CreateUserDTO = { - id?: string -} - -export type UpdateUserDTO = { - id: string -} - -export type FilterableUserProps = {} diff --git a/packages/utils/src/auth/abstract-auth-provider.ts b/packages/utils/src/auth/abstract-auth-provider.ts index 9a1d4682d8..619d1f9d38 100644 --- a/packages/utils/src/auth/abstract-auth-provider.ts +++ b/packages/utils/src/auth/abstract-auth-provider.ts @@ -46,7 +46,7 @@ export abstract class AbstractAuthModuleProvider { const cloned = new (this.constructor as any)(this.container_) cloned.scope_ = scope - cloned.scopeConfg_ = this.scopes_[scope] + cloned.scopeConfig_ = this.scopes_[scope] return cloned } diff --git a/yarn.lock b/yarn.lock index 22b410d874..ff7230c77e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8875,6 +8875,7 @@ __metadata: cross-env: ^5.2.1 dotenv: 16.3.1 jest: ^29.6.3 + jsonwebtoken: ^9.0.2 knex: 2.4.2 medusa-test-utils: ^1.1.40 rimraf: ^3.0.2