diff --git a/backend/src/common/data-injection.tokens.ts b/backend/src/common/data-injection.tokens.ts index c8ce4fc3e..6d6ae5c40 100644 --- a/backend/src/common/data-injection.tokens.ts +++ b/backend/src/common/data-injection.tokens.ts @@ -113,6 +113,7 @@ export enum UseCaseType { SAAS_GET_USER_INFO = 'SAAS_GET_USER_INFO', SAAS_USUAL_REGISTER_USER = 'SAAS_USUAL_REGISTER_USER', SAAS_USUAL_LOGIN_USER = 'SAAS_USUAL_LOGIN_USER', + SAAS_GET_USER_EMAIL_COMPANIES = 'SAAS_GET_USER_EMAIL_COMPANIES', SAAS_DEMO_USER_REGISTRATION = 'SAAS_DEMO_USER_REGISTRATION', SAAS_LOGIN_USER_WITH_GOOGLE = 'SAAS_LOGIN_USER_WITH_GOOGLE', SAAS_LOGIN_USER_WITH_GITHUB = 'SAAS_LOGIN_USER_WITH_GITHUB', diff --git a/backend/src/microservices/saas-microservice/saas.controller.ts b/backend/src/microservices/saas-microservice/saas.controller.ts index 56ce2eaaf..a0cafbc77 100644 --- a/backend/src/microservices/saas-microservice/saas.controller.ts +++ b/backend/src/microservices/saas-microservice/saas.controller.ts @@ -15,6 +15,7 @@ import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nes import { SkipThrottle } from '@nestjs/throttler'; import { UseCaseType } from '../../common/data-injection.tokens.js'; import { Timeout } from '../../decorators/timeout.decorator.js'; +import { FoundUserEmailCompaniesInfoDs } from '../../entities/company-info/application/data-structures/found-company-info.ds.js'; import { CompanyInfoEntity } from '../../entities/company-info/company-info.entity.js'; import { CreatedConnectionDTO } from '../../entities/connection/application/dto/created-connection.dto.js'; import { SaasUsualUserRegisterDS } from '../../entities/user/application/data-structures/usual-register-user.ds.js'; @@ -23,6 +24,7 @@ import { ExternalRegistrationProviderEnum } from '../../entities/user/enums/exte import { UserEntity } from '../../entities/user/user.entity.js'; import { InTransactionEnum } from '../../enums/in-transaction.enum.js'; import { Messages } from '../../exceptions/text/messages.js'; +import { ValidationHelper } from '../../helpers/validators/validation-helper.js'; import { SentryInterceptor } from '../../interceptors/sentry.interceptor.js'; import { CreatedConnectionResponse, SuccessResponse } from './data-structures/common-responce.ds.js'; import { CreateConnectionForHostedDbDto } from './data-structures/create-connecttion-for-selfhosted-db.dto.js'; @@ -51,6 +53,7 @@ import { ISaaSGetCompanyInfoByUserId, ISaaSGetUsersCountInCompany, ISaasDemoRegisterUser, + ISaasGetUserEmailCompanies, ISaasGetUsersInfosByEmail, ISaasRegisterUser, ISaasSAMLRegisterUser, @@ -79,6 +82,8 @@ export class SaasController { private readonly usualRegisterUserUseCase: ISaasRegisterUser, @Inject(UseCaseType.SAAS_USUAL_LOGIN_USER) private readonly usualLoginUserUseCase: ISaasUsualLoginUser, + @Inject(UseCaseType.SAAS_GET_USER_EMAIL_COMPANIES) + private readonly getUserEmailCompaniesUseCase: ISaasGetUserEmailCompanies, @Inject(UseCaseType.SAAS_DEMO_USER_REGISTRATION) private readonly demoRegisterUserUseCase: ISaasDemoRegisterUser, @Inject(UseCaseType.SAAS_LOGIN_USER_WITH_GOOGLE) @@ -194,6 +199,19 @@ export class SaasController { ); } + @ApiOperation({ summary: 'Get companies where a user with this email is registered' }) + @ApiResponse({ + status: 200, + description: 'Companies where a user with this email is registered.', + type: FoundUserEmailCompaniesInfoDs, + isArray: true, + }) + @Get('user/email/:email/companies') + async getUserEmailCompanies(@Param('email') email: string): Promise> { + ValidationHelper.validateOrThrowHttpExceptionEmail(email); + return await this.getUserEmailCompaniesUseCase.execute(email); + } + @ApiOperation({ summary: 'Register demo user register webhook' }) @ApiBody({ type: SaasUsualUserRegisterDS }) @ApiResponse({ diff --git a/backend/src/microservices/saas-microservice/saas.module.ts b/backend/src/microservices/saas-microservice/saas.module.ts index 37b57dfc4..cc3c0a8f0 100644 --- a/backend/src/microservices/saas-microservice/saas.module.ts +++ b/backend/src/microservices/saas-microservice/saas.module.ts @@ -21,6 +21,7 @@ import { LoginWithGoogleUseCase } from './use-cases/login-with-google.use.case.j import { RegisteredCompanyWebhookUseCase } from './use-cases/register-company-webhook.use.case.js'; import { SaasRegisterDemoUserAccountUseCase } from './use-cases/register-demo-user-account.use.case.js'; import { SaaSRegisterUserWIthSamlUseCase } from './use-cases/register-user-with-saml-use.case.js'; +import { SaasGetUserEmailCompaniesUseCase } from './use-cases/saas-get-user-email-companies.use.case.js'; import { SaasUsualLoginUseCase } from './use-cases/saas-usual-login.use.case.js'; import { SaasUsualRegisterUseCase } from './use-cases/saas-usual-register-user.use.case.js'; import { SuspendUsersUseCase } from './use-cases/suspend-users.use.case.js'; @@ -51,6 +52,10 @@ import { UpdateHostedConnectionPasswordUseCase } from './use-cases/update-hosted provide: UseCaseType.SAAS_USUAL_LOGIN_USER, useClass: SaasUsualLoginUseCase, }, + { + provide: UseCaseType.SAAS_GET_USER_EMAIL_COMPANIES, + useClass: SaasGetUserEmailCompaniesUseCase, + }, { provide: UseCaseType.SAAS_LOGIN_USER_WITH_GOOGLE, useClass: LoginWithGoogleUseCase, @@ -130,6 +135,7 @@ export class SaasModule { { path: 'saas/users/email/:userEmail', method: RequestMethod.GET }, { path: 'saas/user/register', method: RequestMethod.POST }, { path: 'saas/user/login', method: RequestMethod.POST }, + { path: 'saas/user/email/:email/companies', method: RequestMethod.GET }, { path: 'saas/user/demo/register', method: RequestMethod.POST }, { path: 'saas/user/google/login', method: RequestMethod.POST }, { path: 'saas/user/github/login', method: RequestMethod.POST }, diff --git a/backend/src/microservices/saas-microservice/use-cases/saas-get-user-email-companies.use.case.ts b/backend/src/microservices/saas-microservice/use-cases/saas-get-user-email-companies.use.case.ts new file mode 100644 index 000000000..517c7eb2c --- /dev/null +++ b/backend/src/microservices/saas-microservice/use-cases/saas-get-user-email-companies.use.case.ts @@ -0,0 +1,41 @@ +import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; +import AbstractUseCase from '../../../common/abstract-use.case.js'; +import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; +import { BaseType } from '../../../common/data-injection.tokens.js'; +import { FoundUserEmailCompaniesInfoDs } from '../../../entities/company-info/application/data-structures/found-company-info.ds.js'; +import { Messages } from '../../../exceptions/text/messages.js'; +import { ISaasGetUserEmailCompanies } from './saas-use-cases.interface.js'; + +/** + * Returns the companies a given email is registered in. This is the SaaS microservice bridge for the + * open-source `GET /my/email/:email` endpoint (GetUserEmailCompaniesUseCase) — duplicated here on + * purpose so rocketadmin-saas can expose the same lookup (used by the multi-company login picker) + * through its own `/saas/*` surface. The open-source endpoint is left untouched. + */ +@Injectable() +export class SaasGetUserEmailCompaniesUseCase + extends AbstractUseCase> + implements ISaasGetUserEmailCompanies +{ + constructor( + @Inject(BaseType.GLOBAL_DB_CONTEXT) + protected _dbContext: IGlobalDatabaseContext, + ) { + super(); + } + + protected async implementation(userEmail: string): Promise> { + const foundCompanies = await this._dbContext.companyInfoRepository.findCompanyInfosByUserEmail( + userEmail.toLowerCase(), + ); + if (!foundCompanies.length) { + throw new HttpException( + { + message: Messages.COMPANIES_USER_EMAIL_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } + return foundCompanies.map(({ id, name }) => ({ id, name })); + } +} diff --git a/backend/src/microservices/saas-microservice/use-cases/saas-use-cases.interface.ts b/backend/src/microservices/saas-microservice/use-cases/saas-use-cases.interface.ts index 8c150f57d..f47378687 100644 --- a/backend/src/microservices/saas-microservice/use-cases/saas-use-cases.interface.ts +++ b/backend/src/microservices/saas-microservice/use-cases/saas-use-cases.interface.ts @@ -1,3 +1,4 @@ +import { FoundUserEmailCompaniesInfoDs } from '../../../entities/company-info/application/data-structures/found-company-info.ds.js'; import { CompanyInfoEntity } from '../../../entities/company-info/company-info.entity.js'; import { CreatedConnectionDTO } from '../../../entities/connection/application/dto/created-connection.dto.js'; import { SaaSRegisterDemoUserAccountDS } from '../../../entities/user/application/data-structures/demo-user-account-register.ds.js'; @@ -45,6 +46,10 @@ export interface ISaasUsualLoginUser { execute(userData: UsualLoginDs, inTransaction?: InTransactionEnum): Promise; } +export interface ISaasGetUserEmailCompanies { + execute(userEmail: string): Promise>; +} + export interface ISaasDemoRegisterUser { execute(userData: SaaSRegisterDemoUserAccountDS): Promise; }