{"id":480730,"date":"2026-05-23T08:35:25","date_gmt":"2026-05-23T08:35:25","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=480730"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=480730","title":{"rendered":"Feature Based Clean Architecture. \u0427\u0430\u0441\u0442\u044c 2: \u0414\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u044b: \u0430\u043d\u0430\u043b\u0438\u0437 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0430"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u0434\u043e\u043a\u0442\u0440\u0438\u043d\u0430 \u0434\u043b\u044f NestJS-\u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432: \u0440\u0430\u0437\u0431\u043e\u0440 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0434\u0435\u0433\u0440\u0430\u0434\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u044b \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0435 \u0435\u0451 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u0440\u043e\u0441\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<\/blockquote>\n<p>\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a \u0447\u0430\u0441\u0442\u0438 1. \u041c\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 <code>AuthService.signUp<\/code> \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0435 \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0449\u0438\u0442\u0435: \u0434\u0432\u0435\u0441\u0442\u0438 \u0441\u0442\u0440\u043e\u043a \u0432 \u043e\u0434\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0448\u0435\u0441\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435, \u0447\u0435\u0442\u044b\u0440\u0435 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0434\u043e\u043c\u0435\u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u0438 \u043f\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0432 \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0418 \u043c\u044b \u0443\u0436\u0435 \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043b\u0438, \u043a\u0430\u043a\u043e\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u043c: \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043f\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c \u2014 <code>UsersService<\/code>, <code>ReferralsService<\/code>, <code>MarketingService<\/code>, <code>FraudService<\/code>, <code>PartnerService<\/code>, \u2014 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0441\u0432\u043e\u044e \u0437\u043e\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438; <code>AuthService<\/code> \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c. \u042d\u0442\u043e\u0442 \u043e\u0442\u0432\u0435\u0442 \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439, \u043f\u0440\u0438\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c NestJS, \u0438 \u0432 \u043b\u044e\u0431\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0443\u0442 \u043a \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0443 \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0439.<\/p>\n<p>\u0427\u0430\u0441\u0442\u044c 2 \u2014 \u043f\u0440\u043e \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442, \u043a\u043e\u0433\u0434\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u044d\u0442\u043e\u0442 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0447\u0435\u0441\u0442\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u0442. \u0421\u043f\u043e\u0439\u043b\u0435\u0440: \u043a\u043e\u0434 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u0438\u044f\u0442\u043d\u0435\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437, \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435, \u043c\u0435\u0442\u043e\u0434 <code>signUp<\/code> \u043f\u043e\u0445\u0443\u0434\u0435\u0435\u0442 \u2014 \u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0441 \u044d\u0442\u0438\u043c \u0432\u0441\u0451, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u043f\u043b\u043e\u0445\u043e \u0432 V3, \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043f\u043b\u043e\u0445\u043e, \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u043d\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0444\u0430\u0441\u043e\u0432\u043a\u0435. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043d\u0443\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0439\u0442\u0438 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c, \u043a\u0430\u043a \u0435\u0433\u043e \u043f\u0440\u043e\u0448\u043b\u0430 \u0431\u044b \u043b\u044e\u0431\u0430\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430.<\/p>\n<p>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u0440\u0435\u0430\u043a\u0446\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043d\u0430 \u043a\u043e\u0434 \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u2014 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043a\u0435\u0442 \u043d\u0430 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433. \u041f\u043b\u0430\u043d \u2014 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043e \u0441\u0432\u043e\u0435\u0439 \u0437\u043e\u043d\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438. <code>AuthService<\/code> \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u043e\u0439 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u0441\u0432\u043e\u0438\u0445 \u0434\u043e\u043c\u0435\u043d\u0430\u0445.<\/p>\n<pre><code class=\"bash\">AuthService (\u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0446\u0438\u044f)\u2502\u251c\u2500\u2500 UsersService        \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u043e\u0438\u0441\u043a \u043f\u043e email, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u251c\u2500\u2500 AntiFraudService    \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0430\u0431\u0443\u0437 (IP, device, \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u043a\u043e\u0440\u0438\u043d\u0433)\u251c\u2500\u2500 ReferralService     \u2014 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0440\u0435\u0444\u0435\u0440\u0430\u043b\u043e\u0432, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0435\u0439, \u043b\u0438\u043c\u0438\u0442\u044b \u0438 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0437\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0439\u251c\u2500\u2500 PartnerService      \u2014 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u0441\u043a\u0438\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c (\u0431\u043b\u043e\u0433\u0435\u0440\u044b, \u0441\u0442\u0440\u0438\u043c\u0435\u0440\u044b, \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u044b) \u0438 \u0440\u0430\u0441\u0447\u0451\u0442 \u0434\u043e\u0445\u043e\u0434\u0430\u251c\u2500\u2500 BonusService        \u2014 \u043d\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u043d\u0443\u0441\u043e\u0432 (\u0440\u0435\u0444\u0435\u0440\u0430\u043b\u044c\u043d\u044b\u0435, \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u0441\u043a\u0438\u0435, \u043c\u043d\u043e\u0433\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435)\u251c\u2500\u2500 AnalyticsService    \u2014 \u0437\u0430\u043f\u0438\u0441\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0439 (\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f, \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u0438, \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f)\u251c\u2500\u2500 AdSourceService     \u2014 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 (\u043f\u043e\u0438\u0441\u043a, \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u044b, A\/B \u0442\u0435\u0441\u0442\u044b)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0441\u0430\u0434\u0438\u0442\u0441\u044f \u0437\u0430 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0441 \u044d\u0442\u0438\u043c \u043f\u043b\u0430\u043d\u043e\u043c \u043d\u0430 \u0440\u0443\u043a\u0430\u0445. \u0422\u0438\u043a\u0435\u0442 \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 \u0440\u0430\u0431\u043e\u0442\u0443, \u043e\u0431\u0432\u0435\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0440\u0435\u0432\u044c\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440\u0430, \u0438 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439 <code>auth.service.ts<\/code> \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0432\u0438\u0434\u0435.<\/p>\n<h4>AuthService.signUp V4<\/h4>\n<pre><code class=\"typescript\">async signUp(  email: string,  password: string,  referralCode?: string,  adSourceCode?: string,  ip?: string,  deviceId?: string,): Promise&lt;SignUpResponse&gt; {  await this.antiFraudService.checkIp(ip);  await this.antiFraudService.checkDevice(deviceId);  await this.antiFraudService.checkBehavior(ip, deviceId);  const adSource = adSourceCode    ? await this.adSourceService.resolve(adSourceCode)    : undefined;  if (adSourceCode &amp;&amp; !adSource) {    throw new BadRequestException(\"Invalid ad source\");  }  if (adSource) {    await this.adSourceService.increment(adSource.id);    await this.analyticsService.trackExperiment({      source: adSource.code,    });  }  const referral = referralCode    ? await this.referralService.getByCode(referralCode)    : undefined;  if (referralCode &amp;&amp; !referral) {    throw new BadRequestException(\"Invalid referral code\");  }  const partnerResult =    referral &amp;&amp; referral.influencerPartner      ? await this.partnerService.processPartner(referral)      : undefined;  const referralOwner =    referral &amp;&amp; !referral.influencerPartner      ? await this.referralService.validateReferral(referral, email)      : undefined;  const existingUserByEmail = await this.usersService.findByEmail(email);  if (existingUserByEmail) {    throw new BadRequestException(\"User already exists\");  }  const newUser = await this.usersService.createUser({    email,    password,    adSource,    ip,    deviceId,  });  if (referralOwner) {    await this.bonusService.giveReferralBonus(referralOwner.id);    await this.referralService.createReferral(referralOwner, newUser);  }  if (partnerResult) {    await this.bonusService.givePartnerReward(      partnerResult.ownerId,      partnerResult.reward,    );    await this.analyticsService.trackPartnerReward(partnerResult);  }  await this.analyticsService.trackRegistration({    userId: newUser.id,    source: adSource?.code,    ip,  });  return {    id: newUser.id,    email: newUser.email,  };}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<blockquote>\n<p><strong>\u041e\u0433\u043e\u0432\u043e\u0440\u043a\u0430 \u043f\u0440\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a.<\/strong> \u0414\u0430\u043b\u044c\u0448\u0435 \u0432 \u043a\u043e\u0434\u0435 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0438\u0440\u0430\u0435\u0442\u0441\u044f <code>signUp<\/code> (\u0432\u0441\u0435 \u0442\u0435, \u0447\u0442\u043e \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0432\u044b\u043d\u0435\u0441\u043b\u0438), \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435 \u0431\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u0430 \u044f\u0432\u043d\u044b\u0439 <code>Result&lt;T, E&gt;<\/code>. \u042d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 <code>.isErr()<\/code> \u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u043a <code>.value<\/code> \u0438\u043b\u0438 <code>.error<\/code>. \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435: \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u0432\u0438\u0434\u0438\u0442 \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0438\u0441\u0445\u043e\u0434\u043e\u0432 \u043f\u0440\u044f\u043c\u043e \u0432 \u0442\u0438\u043f\u0435. \u0421\u0430\u043c <code>signUp<\/code> \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u043e\u0439 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u043c\u0435\u0436\u0434\u0443 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0438 HTTP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043e\u043c \u2014 \u043e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 <code>Result<\/code> \u043e\u0442 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0438 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 <code>HttpException<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e NestJS-\u0444\u0438\u043b\u044c\u0442\u0440 \u043d\u0430 HTTP-\u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445. \u0422\u0430\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u043c, \u0447\u0442\u043e Result-\u0441\u0442\u0438\u043b\u044c \u0438 <code>throw<\/code>-\u0441\u0442\u0438\u043b\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043a\u043e\u043d\u043a\u0443\u0440\u0438\u0440\u0443\u044e\u0442: \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 Result, \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u0435 AuthService \u2014 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 <code>BadRequestException<\/code> \/ <code>ConflictException<\/code> \/ <code>ForbiddenException<\/code> \/ <code>InternalServerErrorException<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 NestJS \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 HTTP-\u043a\u043e\u0434. \u041a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Result \u2014 \u0432\u043e\u043f\u0440\u043e\u0441 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f. \u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043c\u043e\u043d\u0430\u0434\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u043d\u0435 \u043d\u0430 \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0441 \u043d\u0435\u0439 \u0443\u0434\u043e\u0431\u043d\u0435\u0435: \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0441\u0445\u043e\u0434. \u0412\u0441\u0451, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435, \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u043e \u0447\u0435\u0440\u0435\u0437 discriminated unions, \u043b\u044e\u0431\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0441 \u043f\u043e\u0445\u043e\u0436\u0435\u0439 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u043e\u0439 \u0438\u043b\u0438 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0435 <code>try\/catch<\/code> \u2014 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u0441\u043c\u044b\u0441\u043b \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u043d\u0434\u0443\u0441\u0442\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0432 TypeScript \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <code>neverthrow<\/code>, \u044f \u0432 \u043a\u043e\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0451 API. \u0417\u0430\u043c\u0435\u0447\u0443 \u0437\u0430\u0440\u0430\u043d\u0435\u0435: \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 Result \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043b\u0435\u0447\u0438\u0442 \u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u2014 \u043e\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0438\u043c\u044b\u043c\u0438. \u0412\u0441\u0451 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u043b\u0438, \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0430 \u0441\u0432\u043e\u0438\u0445 \u043c\u0435\u0441\u0442\u0430\u0445. \u041f\u0440\u043e\u0441\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u044f\u0442\u0430\u0442\u044c\u0441\u044f \u0437\u0430 <code>throw<\/code>-\u0430\u043c\u0438 \u0432 \u0433\u043b\u0443\u0431\u0438\u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<\/p>\n<\/blockquote>\n<h4>AuthService.signUp V5<\/h4>\n<pre><code class=\"typescript\">async signUp(  email: string,  password: string,  referralCode?: string,  adSourceCode?: string,  ip?: string,  deviceId?: string,): Promise&lt;SignUpResponse&gt; {  const checkIpResult = await this.antiFraudService.checkIp(ip);  if (checkIpResult.isErr()) {    throw new ForbiddenException(\"SIGN_UP_ANTI_FRAUD_REJECTED\");  }  const checkDeviceResult = await this.antiFraudService.checkDevice(deviceId);  if (checkDeviceResult.isErr()) {    throw new ForbiddenException(\"SIGN_UP_ANTI_FRAUD_REJECTED\");  }  const checkBehaviorResult = await this.antiFraudService.checkBehavior(    ip,    deviceId,  );  if (checkBehaviorResult.isErr()) {    throw new ForbiddenException(\"SIGN_UP_ANTI_FRAUD_REJECTED\");  }  const resolveAdSourceResult = adSourceCode    ? await this.adSourceService.resolve(adSourceCode)    : ok(undefined);  if (resolveAdSourceResult.isErr()) {    throw new BadRequestException(\"SIGN_UP_INVALID_AD_SOURCE\");  }  const adSource = resolveAdSourceResult.value;  if (adSource) {    const incrementAdSourceResult = await this.adSourceService.increment(      adSource.id,    );    if (incrementAdSourceResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }    const trackExperimentResult = await this.analyticsService.trackExperiment(      { source: adSource.code },    );    if (trackExperimentResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }  }  const getReferralResult = referralCode    ? await this.referralService.getByCode(referralCode)    : ok(undefined);  if (getReferralResult.isErr()) {    throw new BadRequestException(\"SIGN_UP_INVALID_REFERRAL_CODE\");  }  const referral = getReferralResult.value;  const processPartnerResult =    referral &amp;&amp; referral.influencerPartner      ? await this.partnerService.processPartner(referral)      : ok(undefined);  if (processPartnerResult.isErr()) {    throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");  }  const partnerResult = processPartnerResult.value;  const validateReferralResult =    referral &amp;&amp; !referral.influencerPartner      ? await this.referralService.validateReferral(referral, email)      : ok(undefined);  if (validateReferralResult.isErr()) {    throw new BadRequestException(\"SIGN_UP_REFERRAL_VALIDATION_FAILED\");  }  const referralOwner = validateReferralResult.value;  const findUserResult = await this.usersService.findByEmail(email);  if (findUserResult.isErr()) {    throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");  }  if (findUserResult.value) {    throw new ConflictException(\"SIGN_UP_USER_ALREADY_EXISTS\");  }  const createUserResult = await this.usersService.createUser({    email,    password,    adSource,    ip,    deviceId,  });  if (createUserResult.isErr()) {    if (createUserResult.error === \"CREATE_USER_CONFLICT\") {      throw new ConflictException(\"SIGN_UP_USER_ALREADY_EXISTS\");    }    throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");  }  const newUser = createUserResult.value;  if (referralOwner) {    const giveReferralBonusResult =      await this.bonusService.giveReferralBonus(referralOwner.id);    if (giveReferralBonusResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }    const createReferralResult = await this.referralService.createReferral(      referralOwner,      newUser,    );    if (createReferralResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }  }  if (partnerResult) {    const givePartnerRewardResult =      await this.bonusService.givePartnerReward(        partnerResult.ownerId,        partnerResult.reward,      );    if (givePartnerRewardResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }    const trackPartnerRewardResult =      await this.analyticsService.trackPartnerReward(partnerResult);    if (trackPartnerRewardResult.isErr()) {      throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");    }  }  const trackRegistrationResult =    await this.analyticsService.trackRegistration({      userId: newUser.id,      source: adSource?.code,      ip,    });  if (trackRegistrationResult.isErr()) {    throw new InternalServerErrorException(\"SIGN_UP_INTERNAL_ERROR\");  }  return {    id: newUser.id,    email: newUser.email,  };}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u0443 \u0432\u0435\u0440\u0441\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u0434\u0435\u043c\u043e. \u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 <code>AuthService.signUp<\/code> \u0432\u0441\u0451 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0442\u0430\u043a, \u043a\u0430\u043a \u0438 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u043b\u043e\u0441\u044c: \u043a\u0430\u0436\u0434\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0432\u043e\u044e \u0437\u043e\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0442\u043e\u043d\u043a\u043e\u0439, \u0432 \u043a\u043e\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u0442\u043a\u043d\u0443\u0442\u044c \u043f\u0430\u043b\u044c\u0446\u0435\u043c \u0438 \u0441\u0440\u0430\u0437\u0443 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0433\u0434\u0435 \u0436\u0438\u0432\u0451\u0442 \u0430\u043d\u0442\u0438-\u0444\u0440\u043e\u0434, \u0433\u0434\u0435 \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u044b, \u0433\u0434\u0435 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440 \u043a\u0438\u0432\u0430\u0435\u0442, \u0440\u0435\u0432\u044c\u044e \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0437\u0430 \u043f\u044f\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c \u043c\u0438\u043d\u0443\u0442. \u041d\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u043b\u043e\u0432\u0443\u0448\u043a\u0430 \u043b\u0435\u0436\u0438\u0442 \u043d\u0435 \u0432 <code>AuthService.signUp<\/code> \u2014 \u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0442\u0430\u043c \u043d\u0435 \u043b\u0435\u0436\u0430\u043b\u0430. \u0427\u0442\u043e\u0431\u044b \u0435\u0451 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0442\u044c \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u0442\u0435\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u043b\u0438. \u0412\u043e\u0437\u044c\u043c\u0451\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443 \u2014 <code>UsersService<\/code>.<\/p>\n<h4>\u042d\u0432\u043e\u043b\u044e\u0446\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u044f \u00abUsers\u00bb<\/h4>\n<p>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441 \u0442\u0435\u043c, \u043a\u0430\u043a \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u043b\u0430\u0441\u044c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f, \u0441\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>users<\/code> \u0442\u043e\u0436\u0435 \u043d\u0435 \u0441\u0442\u043e\u044f\u043b \u043d\u0430 \u043c\u0435\u0441\u0442\u0435. \u0424\u0440\u043e\u043d\u0442\u0443 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0444\u0438\u043b\u044f, \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0435 \u2014 \u0441\u0447\u0451\u0442\u0447\u0438\u043a\u0438 \u0438 \u0441\u0440\u0435\u0437\u044b, \u043c\u0430\u0440\u043a\u0435\u0442\u0438\u043d\u0433\u0443 \u2014 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0435 \u2014 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. \u0422\u043e, \u0447\u0442\u043e \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u0431\u044b\u043b\u043e \u043e\u0434\u043d\u0438\u043c \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u0435\u043c \u0441 \u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439, \u043a \u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b\u043e\u0441\u044c \u0432 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d \u0441 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043d\u0430\u0431\u043e\u0440\u043e\u043c use-case\u2019\u043e\u0432. \u041a \u0442\u0435\u043a\u0443\u0449\u0435\u043c\u0443 \u044d\u0442\u0430\u043f\u0443 <code>UsersService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0443\u0436\u0435 \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0437\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u044f (bio, avatar, username)<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 (public \/ private)<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 (\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432 \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a)<\/p>\n<\/li>\n<li>\n<p>\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 (\u044f\u0437\u044b\u043a, \u0442\u0435\u043c\u0430, \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438)<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (<code>\/me<\/code> endpoint)<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"bash\">src\/modules\/users\/\u251c\u2500\u2500 users.module.ts\u251c\u2500\u2500 users.service.ts\u251c\u2500\u2500 users.controller.ts\u251c\u2500\u2500 dto\/\u2502   \u251c\u2500\u2500 get-profile.dto.ts\u2502   \u251c\u2500\u2500 update-profile.dto.ts\u2502   \u251c\u2500\u2500 update-account-settings.dto.ts\u2502   \u251c\u2500\u2500 update-privacy.dto.ts\u2502   \u251c\u2500\u2500 update-preferences.dto.ts\u2502   \u251c\u2500\u2500 get-user-stats.dto.ts\u2502   \u2514\u2500\u2500 me.dto.ts\u2514\u2500\u2500 entities\/    \u251c\u2500\u2500 user.entity.ts    \u251c\u2500\u2500 user-profile.entity.ts    \u251c\u2500\u2500 user-settings.entity.ts    \u251c\u2500\u2500 user-privacy.entity.ts    \u251c\u2500\u2500 user-preferences.entity.ts    \u251c\u2500\u2500 user-stats.entity.ts    \u2514\u2500\u2500 user-session.entity.ts<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0434 \u044d\u0442\u043e\u0442 \u043d\u0430\u0431\u043e\u0440 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0443 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043a\u0430\u0436\u0434\u043e\u043c\u0443 use-case\u2019\u0443 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0440\u0443\u0447\u043a\u0430. \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"typescript\">@Controller(\"users\")export class UsersController {  constructor(private readonly usersService: UsersService) {}  @Get(\":id\/profile\")  async getProfile(@Param() dto: GetProfileDto): Promise&lt;UserProfileResponse&gt; {    return this.usersService.getProfile(dto.userId);  }  @Patch(\":id\/profile\")  async updateProfile(    @Param() params: GetProfileDto,    @Body() dto: UpdateProfileDto,  ): Promise&lt;UserProfileResponse&gt; {    return this.usersService.updateProfile(params.userId, dto);  }  @Patch(\":id\/settings\")  async updateAccountSettings(    @Param() params: GetProfileDto,    @Body() dto: UpdateAccountSettingsDto,  ): Promise&lt;UserAccountSettingsResponse&gt; {    return this.usersService.updateAccountSettings(params.userId, dto);  }  @Patch(\":id\/privacy\")  async updatePrivacy(    @Param() params: GetProfileDto,    @Body() dto: UpdatePrivacyDto,  ): Promise&lt;UserPrivacyResponse&gt; {    return this.usersService.updatePrivacy(params.userId, dto);  }  @Patch(\":id\/preferences\")  async updatePreferences(    @Param() params: GetProfileDto,    @Body() dto: UpdatePreferencesDto,  ): Promise&lt;UserPreferencesResponse&gt; {    return this.usersService.updatePreferences(params.userId, dto);  }  @Get(\":id\/stats\")  async getUserStats(    @Param() dto: GetUserStatsDto,  ): Promise&lt;UserStatsResponse&gt; {    return this.usersService.getUserStats(dto.userId);  }  @Get(\"me\")  async getMe(@Req() req: Request): Promise&lt;CurrentUserResponse&gt; {    return this.usersService.getCurrentUser(req.user.id);  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0437\u0446\u043e\u0432\u043e: \u0441\u0435\u043c\u044c \u0440\u0443\u0447\u0435\u043a \u2014 \u0441\u0435\u043c\u044c \u0437\u043e\u043d \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043a\u0430\u0436\u0434\u0430\u044f \u0441 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c DTO, \u043d\u0438 \u043e\u0434\u043d\u0430 \u043d\u0435 \u043f\u0443\u0442\u0430\u0435\u0442\u0441\u044f \u0441 \u0434\u0440\u0443\u0433\u043e\u0439. \u0412\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u2014 \u0430 \u0447\u0442\u043e \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043e\u043f\u0438\u0440\u0430\u0435\u0442\u0441\u044f? \u041b\u043e\u0433\u0438\u0447\u043d\u043e\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0435: \u0440\u0430\u0437 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u0440\u0430\u0437\u043d\u0435\u0441\u0451\u043d \u043f\u043e use-case\u2019\u0430\u043c, \u0442\u043e \u0438 <code>UsersService<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0435\u0440\u043a\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u044d\u0442\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0437\u043e\u043d\u0430 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u0442\u0430\u043a\u0430\u044f \u0436\u0435 \u0434\u0438\u0441\u0446\u0438\u043f\u043b\u0438\u043d\u0430 \u0432\u043d\u0443\u0442\u0440\u0438. \u0422\u0430\u043a \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0443\u0447\u0435\u0431\u043d\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u0438 \u0442\u0430\u043a \u044d\u0442\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 NestJS. \u041e\u0442\u043a\u0440\u043e\u0435\u043c <code>users.service.ts<\/code> \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f.<\/p>\n<h4>UsersService V1<\/h4>\n<pre><code class=\"typescript\">@Injectable()export class UsersService {  constructor(    @InjectRepository(User)    private readonly userRepository: Repository&lt;User&gt;,    @InjectRepository(UserProfile)    private readonly profileRepository: Repository&lt;UserProfile&gt;,    @InjectRepository(UserSettings)    private readonly settingsRepository: Repository&lt;UserSettings&gt;,    @InjectRepository(UserPrivacy)    private readonly privacyRepository: Repository&lt;UserPrivacy&gt;,    @InjectRepository(UserPreferences)    private readonly preferencesRepository: Repository&lt;UserPreferences&gt;,    @InjectRepository(UserStats)    private readonly statsRepository: Repository&lt;UserStats&gt;,  ) {}  async findByEmail(    email: string,  ): Promise&lt;Result&lt;User | undefined, FindUserErrorCode&gt;&gt; {    const findUserResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.findOne({ where: { email } }),    )();    if (findUserResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(findUserResult.value ?? undefined);  }  async createUser(    data: CreateUserData,  ): Promise&lt;Result&lt;User, CreateUserErrorCode&gt;&gt; {    const newUser = this.userRepository.create({      email: data.email,      password: data.password,      registrationIp: data.ip,      deviceId: data.deviceId,      adSource: data.adSource,      isVerified: false,    });    const saveUserResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.save(newUser),    )();    if (saveUserResult.isErr()) {      if (isUniqueQueryError(saveUserResult.error)) {        return err(\"CREATE_USER_CONFLICT\");      }      return err(\"CREATE_USER_DATABASE_ERROR\");    }    const initUserRelationsResult = await fromAsyncThrowable(async () =&gt;      Promise.all([        this.profileRepository.save({ userId: newUser.id }),        this.settingsRepository.save({ userId: newUser.id }),        this.privacyRepository.save({ userId: newUser.id }),        this.preferencesRepository.save({ userId: newUser.id }),        this.statsRepository.save({ userId: newUser.id }),      ]),    )();    if (initUserRelationsResult.isErr()) {      return err(\"CREATE_USER_DATABASE_ERROR\");    }    return ok(newUser);  }  async getProfile(userId: string): Promise&lt;UserProfile&gt; {    const profile = await this.profileRepository.findOne({ where: { userId } });    if (!profile) {      throw new NotFoundException(\"USER_PROFILE_NOT_FOUND\");    }    return profile;  }  async updateProfile(    userId: string,    dto: UpdateProfileDto,  ): Promise&lt;UserProfile&gt; {    await this.profileRepository.update({ userId }, dto);    return this.getProfile(userId);  }  async updateAccountSettings(    userId: string,    dto: UpdateAccountSettingsDto,  ): Promise&lt;UserSettings&gt; {    await this.settingsRepository.update({ userId }, dto);    const settings = await this.settingsRepository.findOne({      where: { userId },    });    if (!settings) {      throw new NotFoundException(\"USER_SETTINGS_NOT_FOUND\");    }    return settings;  }  async updatePrivacy(    userId: string,    dto: UpdatePrivacyDto,  ): Promise&lt;UserPrivacy&gt; {    await this.privacyRepository.update({ userId }, dto);    const privacy = await this.privacyRepository.findOne({      where: { userId },    });    if (!privacy) {      throw new NotFoundException(\"USER_PRIVACY_NOT_FOUND\");    }    return privacy;  }  async updatePreferences(    userId: string,    dto: UpdatePreferencesDto,  ): Promise&lt;UserPreferences&gt; {    await this.preferencesRepository.update({ userId }, dto);    const preferences = await this.preferencesRepository.findOne({      where: { userId },    });    if (!preferences) {      throw new NotFoundException(\"USER_PREFERENCES_NOT_FOUND\");    }    return preferences;  }  async getUserStats(userId: string): Promise&lt;UserStats&gt; {    const stats = await this.statsRepository.findOne({ where: { userId } });    if (!stats) {      throw new NotFoundException(\"USER_STATS_NOT_FOUND\");    }    return stats;  }  async getCurrentUser(userId: string): Promise&lt;CurrentUserResponse&gt; {    const [profile, settings, privacy, preferences, stats] = await Promise.all([      this.profileRepository.findOne({ where: { userId } }),      this.settingsRepository.findOne({ where: { userId } }),      this.privacyRepository.findOne({ where: { userId } }),      this.preferencesRepository.findOne({ where: { userId } }),      this.statsRepository.findOne({ where: { userId } }),    ]);    if (!profile || !settings || !privacy || !preferences || !stats) {      throw new NotFoundException(\"USER_NOT_FOUND\");    }    return { profile, settings, privacy, preferences, stats };  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e <code>UsersService<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u043e: \u0442\u0438\u043f\u044b \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b, \u043e\u0448\u0438\u0431\u043a\u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f, \u0438\u043c\u0435\u043d\u0430 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f. \u041d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0432 \u044d\u0442\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u0441\u0438\u0433\u043d\u0430\u043b, \u0440\u0430\u0434\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043e\u0442\u043a\u0440\u044b\u043b\u0438 \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u043f\u0435\u0440\u0432\u044b\u043c. \u0423 <code>UsersService<\/code> \u043e\u0441\u043e\u0431\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441, \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0449\u0438\u0439 \u0435\u0433\u043e \u043e\u0442 \u043b\u044e\u0431\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435: \u044d\u0442\u043e <strong>\u043d\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 \u0444\u0438\u0447\u0438, \u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong> \u2014 \u043e\u043d \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u043e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0430\u043c\u0443 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0442\u0430\u043a \u0438\u043b\u0438 \u0438\u043d\u0430\u0447\u0435 \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0418 \u0438\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u043a\u0440\u0443\u0433 \u043d\u0435\u0433\u043e \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u0432\u044b\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u044c.<\/p>\n<p><code>AuthService<\/code> \u0443\u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u2014 \u043e\u043d \u0437\u0430\u0448\u0451\u043b \u043f\u0435\u0440\u0432\u044b\u043c, \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043c\u044b \u044d\u0442\u043e \u0432\u0438\u0434\u0435\u043b\u0438 \u0432 <code>signUp V4\/V5<\/code>. \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0442\u043e\u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u2014 \u043e\u043d \u043e\u0442\u0434\u0430\u0451\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0435\u0433\u043e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u0412 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u043f\u0440\u0438\u043d\u0442\u043e\u0432 \u0432 \u044d\u0442\u0443 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0432\u0441\u0442\u0430\u043d\u0443\u0442 \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430. <code>Feed<\/code> \u0437\u0430\u0445\u043e\u0447\u0435\u0442 \u0437\u043d\u0430\u0442\u044c, \u043d\u0430 \u043a\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d \u0438 \u043a\u043e\u043c\u0443 \u043e\u043d \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0435\u0431\u044f. <code>Notifications<\/code> \u2014 \u043a\u0443\u0434\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c push, \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u043b\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0438 \u043d\u0435 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c. <code>Comments<\/code>, <code>Likes<\/code> \u0438 <code>Follows<\/code> \u2014 \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u0447\u0442\u043e \u043e\u043d \u043d\u0435 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 (\u0438\u043b\u0438 \u0447\u0442\u043e \u0437\u0440\u0438\u0442\u0435\u043b\u044c \u043d\u0430 \u043d\u0435\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d), \u043f\u043b\u044e\u0441 <code>username<\/code> \u0438 <code>avatar<\/code> \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. <code>Search<\/code> \u2014 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0434\u0430\u0447\u0443 \u043f\u043e \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438 \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0444\u0438\u043b\u044c. <code>Media<\/code> \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443. <code>Moderation<\/code> \u0438 \u0430\u043d\u0442\u0438-\u0444\u0440\u043e\u0434 \u2014 \u0441\u0442\u0430\u0442\u0443\u0441, \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u0438\u0441\u0442\u043e\u0440\u0438\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439. \u0418 \u0432\u0441\u0435 \u044d\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u2014 \u0432\u0441\u0435, \u0431\u0435\u0437 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u2014 \u043f\u0440\u0438\u0437\u0435\u043c\u043b\u044f\u0442\u0441\u044f \u0432 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u0444\u0430\u0439\u043b.<\/p>\n<h4>UsersService V2<\/h4>\n<pre><code class=\"typescript\">@Injectable()export class UsersService {  constructor(    @InjectRepository(User)    private readonly userRepository: Repository&lt;User&gt;,    @InjectRepository(UserProfile)    private readonly profileRepository: Repository&lt;UserProfile&gt;,    @InjectRepository(UserSettings)    private readonly settingsRepository: Repository&lt;UserSettings&gt;,    @InjectRepository(UserPrivacy)    private readonly privacyRepository: Repository&lt;UserPrivacy&gt;,    @InjectRepository(UserPreferences)    private readonly preferencesRepository: Repository&lt;UserPreferences&gt;,    @InjectRepository(UserStats)    private readonly statsRepository: Repository&lt;UserStats&gt;,  ) {}  async findByEmail(    email: string,  ): Promise&lt;Result&lt;User | undefined, FindUserErrorCode&gt;&gt; {    const findUserResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.findOne({ where: { email } }),    )();    if (findUserResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(findUserResult.value ?? undefined);  }  async createUser(    data: CreateUserData,  ): Promise&lt;Result&lt;User, CreateUserErrorCode&gt;&gt; {    const newUser = this.userRepository.create({      email: data.email,      password: data.password,      registrationIp: data.ip,      deviceId: data.deviceId,      adSource: data.adSource,      isVerified: false,    });    const saveUserResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.save(newUser),    )();    if (saveUserResult.isErr()) {      if (isUniqueQueryError(saveUserResult.error)) {        return err(\"CREATE_USER_CONFLICT\");      }      return err(\"CREATE_USER_DATABASE_ERROR\");    }    const initUserRelationsResult = await fromAsyncThrowable(async () =&gt;      Promise.all([        this.profileRepository.save({ userId: newUser.id }),        this.settingsRepository.save({ userId: newUser.id }),        this.privacyRepository.save({ userId: newUser.id }),        this.preferencesRepository.save({ userId: newUser.id }),        this.statsRepository.save({ userId: newUser.id }),      ]),    )();    if (initUserRelationsResult.isErr()) {      return err(\"CREATE_USER_DATABASE_ERROR\");    }    return ok(newUser);  }  async exists(userId: string): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    const checkExistsResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.exist({ where: { id: userId } }),    )();    if (checkExistsResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(checkExistsResult.value);  }  async getProfile(userId: string): Promise&lt;UserProfile&gt; {    const profile = await this.profileRepository.findOne({ where: { userId } });    if (!profile) {      throw new NotFoundException(\"USER_PROFILE_NOT_FOUND\");    }    return profile;  }  async updateProfile(    userId: string,    dto: UpdateProfileDto,  ): Promise&lt;UserProfile&gt; {    await this.profileRepository.update({ userId }, dto);    return this.getProfile(userId);  }  async updateAccountSettings(    userId: string,    dto: UpdateAccountSettingsDto,  ): Promise&lt;UserSettings&gt; {    await this.settingsRepository.update({ userId }, dto);    const settings = await this.settingsRepository.findOne({      where: { userId },    });    if (!settings) {      throw new NotFoundException(\"USER_SETTINGS_NOT_FOUND\");    }    return settings;  }  async updatePrivacy(    userId: string,    dto: UpdatePrivacyDto,  ): Promise&lt;UserPrivacy&gt; {    await this.privacyRepository.update({ userId }, dto);    const privacy = await this.privacyRepository.findOne({      where: { userId },    });    if (!privacy) {      throw new NotFoundException(\"USER_PRIVACY_NOT_FOUND\");    }    return privacy;  }  async updatePreferences(    userId: string,    dto: UpdatePreferencesDto,  ): Promise&lt;UserPreferences&gt; {    await this.preferencesRepository.update({ userId }, dto);    const preferences = await this.preferencesRepository.findOne({      where: { userId },    });    if (!preferences) {      throw new NotFoundException(\"USER_PREFERENCES_NOT_FOUND\");    }    return preferences;  }  async getUserStats(userId: string): Promise&lt;UserStats&gt; {    const stats = await this.statsRepository.findOne({ where: { userId } });    if (!stats) {      throw new NotFoundException(\"USER_STATS_NOT_FOUND\");    }    return stats;  }  async getCurrentUser(userId: string): Promise&lt;CurrentUserResponse&gt; {    const [profile, settings, privacy, preferences, stats] = await Promise.all([      this.profileRepository.findOne({ where: { userId } }),      this.settingsRepository.findOne({ where: { userId } }),      this.privacyRepository.findOne({ where: { userId } }),      this.preferencesRepository.findOne({ where: { userId } }),      this.statsRepository.findOne({ where: { userId } }),    ]);    if (!profile || !settings || !privacy || !preferences || !stats) {      throw new NotFoundException(\"USER_NOT_FOUND\");    }    return { profile, settings, privacy, preferences, stats };  }  async getFollowingIds(    userId: string,  ): Promise&lt;Result&lt;string[], FindUserErrorCode&gt;&gt; {    return ok([]);  }  async canViewContent(    viewerId: string,    ownerId: string,  ): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    const isPrivateResult = await this.isPrivate(ownerId);    if (isPrivateResult.isErr()) {      return err(isPrivateResult.error);    }    if (!isPrivateResult.value) {      return ok(true);    }    const getFollowingResult = await this.getFollowingIds(viewerId);    if (getFollowingResult.isErr()) {      return err(getFollowingResult.error);    }    return ok(getFollowingResult.value.includes(ownerId));  }  async canReceiveNotification(    userId: string,    type: string,  ): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    const findSettingsResult = await this.findUserSettings(userId);    if (findSettingsResult.isErr()) {      return err(findSettingsResult.error);    }    const settings = findSettingsResult.value;    if (!settings) return ok(false);    if (type === \"email\") return ok(settings.emailNotifications);    if (type === \"push\") return ok(settings.pushNotifications);    return ok(false);  }  async getPublicUserInfo(    userId: string,  ): Promise&lt;Result&lt;UserPublicInfo, FindUserErrorCode&gt;&gt; {    const findProfileResult = await fromAsyncThrowable(async () =&gt;      this.profileRepository.findOne({ where: { userId } }),    )();    if (findProfileResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok({      id: userId,      username: findProfileResult.value?.username,      avatarUrl: findProfileResult.value?.avatarUrl,    });  }  async isSearchable(    userId: string,  ): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    const findPrivacyResult = await this.findUserPrivacy(userId);    if (findPrivacyResult.isErr()) {      return err(findPrivacyResult.error);    }    return ok(!findPrivacyResult.value?.isPrivate);  }  async isUserBlocked(    userId: string,  ): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    return ok(false);  }  async getUserStatus(    userId: string,  ): Promise&lt;Result&lt;UserStatus | undefined, FindUserErrorCode&gt;&gt; {    const findUserResult = await fromAsyncThrowable(async () =&gt;      this.userRepository.findOne({        where: { id: userId },        select: [\"id\", \"isVerified\"],      }),    )();    if (findUserResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(findUserResult.value ?? undefined);  }  private async findUserSettings(    userId: string,  ): Promise&lt;Result&lt;UserSettings | undefined, FindUserErrorCode&gt;&gt; {    const findSettingsResult = await fromAsyncThrowable(async () =&gt;      this.settingsRepository.findOne({ where: { userId } }),    )();    if (findSettingsResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(findSettingsResult.value ?? undefined);  }  private async findUserPrivacy(    userId: string,  ): Promise&lt;Result&lt;UserPrivacy | undefined, FindUserErrorCode&gt;&gt; {    const findPrivacyResult = await fromAsyncThrowable(async () =&gt;      this.privacyRepository.findOne({ where: { userId } }),    )();    if (findPrivacyResult.isErr()) {      return err(\"FIND_USER_DATABASE_ERROR\");    }    return ok(findPrivacyResult.value ?? undefined);  }  private async isPrivate(    userId: string,  ): Promise&lt;Result&lt;boolean, FindUserErrorCode&gt;&gt; {    const findPrivacyResult = await this.findUserPrivacy(userId);    if (findPrivacyResult.isErr()) {      return err(findPrivacyResult.error);    }    return ok(!!findPrivacyResult.value?.isPrivate);  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u0442 \u0436\u0435 \u0440\u0438\u0442\u0443\u0430\u043b, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u044e\u0431\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0440\u0430\u0437\u0434\u0443\u0442\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u2014 \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0432\u0438\u0434\u0435\u043b \u0432 \u044d\u0442\u043e\u0439 \u0436\u0435 \u0441\u0442\u0430\u0442\u044c\u0435 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435. \u0414\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0439 \u0438 \u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0439, \u043d\u0430 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0434\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442: <code>UsersService<\/code> \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b\u0441\u044f \u0432 \u0444\u0430\u0439\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u0445\u043e\u0447\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0432 \u043e\u0434\u0438\u043d\u043e\u0447\u043a\u0443, \u0438 \u043b\u044e\u0431\u043e\u0439 \u043d\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u0442 \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435: \u00ab\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u044f \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0443\u00bb. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u044d\u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u0441\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u043e \u0437\u043d\u0430\u043a\u043e\u043c \u2014 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0451 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u0420\u0430\u0437 \u043e\u0434\u0438\u043d \u043a\u043b\u0430\u0441\u0441 \u0432\u044b\u0440\u043e\u0441 \u043d\u0435\u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, \u0440\u0430\u0437\u043d\u0435\u0441\u0451\u043c \u0435\u0433\u043e \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u043d\u044c\u0448\u0438\u0445, \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043e\u0442\u0434\u0430\u0434\u0438\u043c \u0441\u0432\u043e\u0439 \u043a\u0443\u0441\u043e\u043a. \u0422\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u0447\u0442\u043e \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u0430\u0436\u0443\u0442\u0441\u044f \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c\u0438 \u2014 \u0442\u0435 \u0436\u0435 \u0441\u0430\u043c\u044b\u0435 use-case\u2019\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440:<\/p>\n<ul>\n<li>\n<p>\u043f\u0440\u043e\u0444\u0438\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u044c<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043b\u0430\u043d \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0440\u043e\u0432\u043d\u043e \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043f\u043e \u0444\u043e\u0440\u043c\u0435, \u043a\u0430\u043a \u0442\u043e\u0442, \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u043b\u044f <code>AuthService<\/code> \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043d\u0430\u0437\u0430\u0434:<\/p>\n<pre><code class=\"bash\">src\/modules\/users\/\u251c\u2500\u2500 users.module.ts\u251c\u2500\u2500 users.controller.ts\u2502\u251c\u2500\u2500 services\/\u2502   \u251c\u2500\u2500 users.service.ts\u2502   \u251c\u2500\u2500 user-profile.service.ts\u2502   \u251c\u2500\u2500 user-settings.service.ts\u2502   \u251c\u2500\u2500 user-privacy.service.ts\u2502   \u251c\u2500\u2500 user-preferences.service.ts\u2502   \u251c\u2500\u2500 user-stats.service.ts\u2502   \u2514\u2500\u2500 user-access.service.ts\u2502\u251c\u2500\u2500 dto\/\u2502   \u251c\u2500\u2500 get-profile.dto.ts\u2502   \u251c\u2500\u2500 update-profile.dto.ts\u2502   \u251c\u2500\u2500 update-account-settings.dto.ts\u2502   \u251c\u2500\u2500 update-privacy.dto.ts\u2502   \u251c\u2500\u2500 update-preferences.dto.ts\u2502   \u251c\u2500\u2500 get-user-stats.dto.ts\u2502   \u2514\u2500\u2500 me.dto.ts\u2502\u2514\u2500\u2500 entities\/    \u251c\u2500\u2500 user.entity.ts    \u251c\u2500\u2500 user-profile.entity.ts    \u251c\u2500\u2500 user-settings.entity.ts    \u251c\u2500\u2500 user-privacy.entity.ts    \u251c\u2500\u2500 user-preferences.entity.ts    \u251c\u2500\u2500 user-stats.entity.ts    \u2514\u2500\u2500 user-session.entity.ts<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0440\u043e\u0434\u0435 \u0431\u044b \u0441\u0442\u0430\u043b\u043e \u043b\u0443\u0447\u0448\u0435:<\/p>\n<ul>\n<li>\n<p><code>UserProfileService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u043e\u0444\u0438\u043b\u044c<\/p>\n<\/li>\n<li>\n<p><code>UserSettingsService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/p>\n<\/li>\n<li>\n<p><code>UserPrivacyService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u044c<\/p>\n<\/li>\n<li>\n<p><code>UserPreferencesService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p><code>UserStatsService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443<\/p>\n<\/li>\n<li>\n<p><code>UserAccessService<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u0430\u0436\u0435\u0442\u0441\u044f, \u043c\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e \u043d\u0430\u0432\u0435\u043b\u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a. \u041a\u0430\u0436\u0434\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0432\u043e\u0439 \u043a\u0443\u0441\u043e\u043a, \u0444\u0430\u0439\u043b\u044b \u043a\u043e\u0440\u043e\u0447\u0435, \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u043b\u043e\u0441\u043a\u0438\u0435, \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430 \u0441\u0445\u0435\u043c\u0435 \u0440\u0438\u0441\u0443\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0430\u043c\u0438 \u0432 \u043e\u0434\u043d\u0443 \u0441\u0442\u043e\u0440\u043e\u043d\u0443. \u041d\u0430 \u0434\u0435\u043c\u043e \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a \u043f\u043e\u0431\u0435\u0434\u0430.<\/p>\n<p>\u0418 \u0438\u043c\u0435\u043d\u043d\u043e \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0438\u043a\u0435\u0442, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0432\u0441\u0451 \u044d\u0442\u043e \u043d\u0430\u0447\u043d\u0451\u0442 \u043b\u043e\u043c\u0430\u0442\u044c\u0441\u044f. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u0432\u043e\u0440\u043e\u0442\u043e\u0432 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0435\u0449\u0451 \u043e\u0434\u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u0430\u044f \u0444\u0438\u0447\u0430, \u0434\u0435\u043d\u044c \u0440\u0430\u0431\u043e\u0442\u044b. \u0420\u0430\u0437\u0431\u0435\u0440\u0451\u043c.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1038416\/\">https:\/\/habr.com\/ru\/articles\/1038416\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u0434\u043e\u043a\u0442\u0440\u0438\u043d\u0430 \u0434\u043b\u044f NestJS-\u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432: \u0440\u0430\u0437\u0431\u043e\u0440 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0434\u0435\u0433\u0440\u0430\u0434\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u044b \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0435 \u0435\u0451 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u0440\u043e\u0441\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a \u0447\u0430\u0441\u0442\u0438 1. \u041c\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 AuthService.signUp \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0435 \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0449\u0438\u0442\u0435: \u0434\u0432\u0435\u0441\u0442\u0438 \u0441\u0442\u0440\u043e\u043a \u0432 \u043e\u0434\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0448\u0435\u0441\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435, \u0447\u0435\u0442\u044b\u0440\u0435 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0434\u043e\u043c\u0435\u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u0438 \u043f\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0432 \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0418 \u043c\u044b \u0443\u0436\u0435 \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043b\u0438, \u043a\u0430\u043a\u043e\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u043c: \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043f\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c \u2014 UsersService, ReferralsService, MarketingService, FraudService, PartnerService, \u2014 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0441\u0432\u043e\u044e \u0437\u043e\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438; AuthService \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c. \u042d\u0442\u043e\u0442 \u043e\u0442\u0432\u0435\u0442 \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439, \u043f\u0440\u0438\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c NestJS, \u0438 \u0432 \u043b\u044e\u0431\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0443\u0442 \u043a \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0443 \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0439.\u0427\u0430\u0441\u0442\u044c 2 \u2014 \u043f\u0440\u043e \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442, \u043a\u043e\u0433\u0434\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u044d\u0442\u043e\u0442 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0447\u0435\u0441\u0442\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u0442. \u0421\u043f\u043e\u0439\u043b\u0435\u0440: \u043a\u043e\u0434 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u0438\u044f\u0442\u043d\u0435\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437, \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435, \u043c\u0435\u0442\u043e\u0434 signUp \u043f\u043e\u0445\u0443\u0434\u0435\u0435\u0442 \u2014 \u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0441 \u044d\u0442\u0438\u043c \u0432\u0441\u0451, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u043f\u043b\u043e\u0445\u043e \u0432 V3, \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043f\u043b\u043e\u0445\u043e, \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u043d\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0444\u0430\u0441\u043e\u0432\u043a\u0435. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043d\u0443\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0439\u0442\u0438 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c, \u043a\u0430\u043a \u0435\u0433\u043e \u043f\u0440\u043e\u0448\u043b\u0430 \u0431\u044b \u043b\u044e\u0431\u0430\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430.\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u0440\u0435\u0430\u043a\u0446\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043d\u0430 \u043a\u043e\u0434 \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u2014 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043a\u0435\u0442 \u043d\u0430 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433. \u041f\u043b\u0430\u043d \u2014 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043e \u0441\u0432\u043e\u0435\u0439 \u0437\u043e\u043d\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438. AuthService \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u043e\u0439 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u0441\u0432\u043e\u0438\u0445 \u0434\u043e\u043c\u0435\u043d\u0430\u0445.AuthService (\u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0446\u0438\u044f)\u2502\u251c\u2500\u2500 UsersService        \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u043e\u0438\u0441\u043a \u043f\u043e email, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u251c\u2500\u2500 AntiFraudService    \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0430\u0431\u0443\u0437 (IP, device, \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u043a\u043e\u0440\u0438\u043d\u0433)\u251c\u2500\u2500 ReferralService     \u2014 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0440\u0435\u0444\u0435\u0440\u0430\u043b\u043e\u0432, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0435\u0439, \u043b\u0438\u043c\u0438\u0442\u044b \u0438 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0437\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0439\u251c\u2500\u2500 PartnerService      \u2014 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u0441\u043a\u0438\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c (\u0431\u043b\u043e\u0433\u0435\u0440\u044b, \u0441\u0442\u0440\u0438\u043c\u0435\u0440\u044b, \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u044b) \u0438 \u0440\u0430\u0441\u0447\u0451\u0442 \u0434\u043e\u0445\u043e\u0434\u0430\u251c\u2500\u2500 BonusService        \u2014 \u043d\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u043d\u0443\u0441\u043e\u0432 (\u0440\u0435\u0444\u0435\u0440\u0430\u043b\u044c\u043d\u044b\u0435, \u043f\u0430\u0440\u0442\u043d\u0451\u0440\u0441\u043a\u0438\u0435, \u043c\u043d\u043e\u0433\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435)\u251c\u2500\u2500 AnalyticsService    \u2014 \u0437\u0430\u043f\u0438\u0441\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0439 (\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f, \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u0438, \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f)\u251c\u2500\u2500 AdSourceService     \u2014 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 (\u043f\u043e\u0438\u0441\u043a, \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u044b, A\/B \u0442\u0435\u0441\u0442\u044b)\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0441\u0430\u0434\u0438\u0442\u0441\u044f \u0437\u0430 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0441 \u044d\u0442\u0438\u043c \u043f\u043b\u0430\u043d\u043e\u043c \u043d\u0430 \u0440\u0443\u043a\u0430\u0445. \u0422\u0438\u043a\u0435\u0442 \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 \u0440\u0430\u0431\u043e\u0442\u0443, \u043e\u0431\u0432\u0435\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0440\u0435\u0432\u044c\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440\u0430, \u0438 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439 auth.service.ts \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0432\u0438\u0434\u0435.AuthService.signUp V4async signUp(  email: string,  password: string,  referralCode?: string,  adSourceCode?: string,  ip?: string,  deviceId?: string,): Promise&lt;SignUpResponse&gt; {  await this.antiFraudService.checkIp(ip);  await this.antiFraudService.checkDevice(deviceId);  await this.antiFraudService.checkBehavior(ip, deviceId);  const adSource = adSourceCode    ? await this.adSourceService.resolve(adSourceCode)    : undefined;  if (adSourceCode &amp;&amp; !adSource) {    throw new BadRequestException(&#171;Invalid ad source&#187;);  }  if (adSource) {    await this.adSourceService.increment(adSource.id);    await this.analyticsService.trackExperiment({      source: adSource.code,    });  }  const referral = referralCode    ? await this.referralService.getByCode(referralCode)    : undefined;  if (referralCode &amp;&amp; !referral) {    throw new BadRequestException(&#171;Invalid referral code&#187;);  }  const partnerResult =    referral &amp;&amp; referral.influencerPartner      ? await this.partnerService.processPartner(referral)      : undefined;  const referralOwner =    referral &amp;&amp; !referral.influencerPartner      ? await this.referralService.validateReferral(referral, email)      : undefined;  const existingUserByEmail = await this.usersService.findByEmail(email);  if (existingUserByEmail) {    throw new BadRequestException(&#171;User already exists&#187;);  }  const newUser = await this.usersService.createUser({    email,    password,    adSource,    ip,    deviceId,  });  if (referralOwner) {    await this.bonusService.giveReferralBonus(referralOwner.id);    await this.referralService.createReferral(referralOwner, newUser);  }  if (partnerResult) {    await this.bonusService.givePartnerReward(      partnerResult.ownerId,      partnerResult.reward,    );    await this.analyticsService.trackPartnerReward(partnerResult);  }  await this.analyticsService.trackRegistration({    userId: newUser.id,    source: adSource?.code,    ip,  });  return {    id: newUser.id,    email: newUser.email,  };}\u041e\u0433\u043e\u0432\u043e\u0440\u043a\u0430 \u043f\u0440\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a. \u0414\u0430\u043b\u044c\u0448\u0435 \u0432 \u043a\u043e\u0434\u0435 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0438\u0440\u0430\u0435\u0442\u0441\u044f signUp (\u0432\u0441\u0435 \u0442\u0435, \u0447\u0442\u043e \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0432\u044b\u043d\u0435\u0441\u043b\u0438), \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435 \u0431\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u0430 \u044f\u0432\u043d\u044b\u0439 Result&lt;T, E&gt;. \u042d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 .isErr() \u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u043a .value \u0438\u043b\u0438 .error. \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435: \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u0432\u0438\u0434\u0438\u0442 \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0438\u0441\u0445\u043e\u0434\u043e\u0432 \u043f\u0440\u044f\u043c\u043e \u0432 \u0442\u0438\u043f\u0435. \u0421\u0430\u043c signUp \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u043e\u0439 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u043c\u0435\u0436\u0434\u0443 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0438 HTTP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043e\u043c \u2014 \u043e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 Result \u043e\u0442 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0438 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 HttpException, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e NestJS-\u0444\u0438\u043b\u044c\u0442\u0440 \u043d\u0430 HTTP-\u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445. \u0422\u0430\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u043c, \u0447\u0442\u043e Result-\u0441\u0442\u0438\u043b\u044c \u0438 throw-\u0441\u0442\u0438\u043b\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043a\u043e\u043d\u043a\u0443\u0440\u0438\u0440\u0443\u044e\u0442: \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 Result, \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u0435 AuthService \u2014 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 BadRequestException \/ ConflictException \/ ForbiddenException \/ InternalServerErrorException, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 NestJS \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 HTTP-\u043a\u043e\u0434. \u041a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Result \u2014 \u0432\u043e\u043f\u0440\u043e\u0441 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f. \u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043c\u043e\u043d\u0430\u0434\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u043d\u0435 \u043d\u0430 \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0441 \u043d\u0435\u0439 \u0443\u0434\u043e\u0431\u043d\u0435\u0435: \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0441\u0445\u043e\u0434. \u0412\u0441\u0451, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435, \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u043e \u0447\u0435\u0440\u0435\u0437 discriminated unions, \u043b\u044e\u0431\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0441 \u043f\u043e\u0445\u043e\u0436\u0435\u0439 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u043e\u0439 \u0438\u043b\u0438 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0435 try\/catch \u2014 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u0441\u043c\u044b\u0441\u043b \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u043d\u0434\u0443\u0441\u0442\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0432 TypeScript \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 neverthrow, \u044f \u0432 \u043a\u043e\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0451 API. \u0417\u0430\u043c\u0435\u0447\u0443 \u0437\u0430\u0440\u0430\u043d\u0435\u0435: \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 Result \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043b\u0435\u0447\u0438\u0442 \u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u2014 \u043e\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0438\u043c\u044b\u043c\u0438. \u0412\u0441\u0451 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u043b\u0438, \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0430 \u0441\u0432\u043e\u0438\u0445 \u043c\u0435\u0441\u0442\u0430\u0445. \u041f\u0440\u043e\u0441\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u044f\u0442\u0430\u0442\u044c\u0441\u044f \u0437\u0430 throw-\u0430\u043c\u0438 \u0432 \u0433\u043b\u0443\u0431\u0438\u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.AuthService.signUp V5async signUp(  email: string,  password: string,  referralCode?: string,  adSourceCode?: string,  ip?: string,  deviceId?: string,): Promise&lt;SignUpResponse&gt; {  const checkIpResult = await this.antiFraudService.checkIp(ip);  if (checkIpResult.isErr()) {    throw new ForbiddenException(&#171;SIGN_UP_ANTI_FRAUD_REJECTED&#187;);  }  const checkDeviceResult = await this.antiFraudService.checkDevice(deviceId);  if (checkDeviceResult.isErr()) {    throw new ForbiddenException(&#171;SIGN_UP_ANTI_FRAUD_REJECTED&#187;);  }  const checkBehaviorResult = await this.antiFraudService.checkBehavior(    ip,    deviceId,  );  if (checkBehaviorResult.isErr()) {    throw new ForbiddenException(&#171;SIGN_UP_ANTI_FRAUD_REJECTED&#187;);  }  const resolveAdSourceResult = adSourceCode    ? await this.adSourceService.resolve(adSourceCode)    : ok(undefined);  if (resolveAdSourceResult.isErr()) {    throw new BadRequestException(&#171;SIGN_UP_INVALID_AD_SOURCE&#187;);  }  const adSource = resolveAdSourceResult.value;  if (adSource) {    const incrementAdSourceResult = await this.adSourceService.increment(      adSource.id,    );    if (incrementAdSourceResult.isErr()) {      throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);    }    const trackExperimentResult = await this.analyticsService.trackExperiment(      { source: adSource.code },    );    if (trackExperimentResult.isErr()) {      throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);    }  }  const getReferralResult = referralCode    ? await this.referralService.getByCode(referralCode)    : ok(undefined);  if (getReferralResult.isErr()) {    throw new BadRequestException(&#171;SIGN_UP_INVALID_REFERRAL_CODE&#187;);  }  const referral = getReferralResult.value;  const processPartnerResult =    referral &amp;&amp; referral.influencerPartner      ? await this.partnerService.processPartner(referral)      : ok(undefined);  if (processPartnerResult.isErr()) {    throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);  }  const partnerResult = processPartnerResult.value;  const validateReferralResult =    referral &amp;&amp; !referral.influencerPartner      ? await this.referralService.validateReferral(referral, email)      : ok(undefined);  if (validateReferralResult.isErr()) {    throw new BadRequestException(&#171;SIGN_UP_REFERRAL_VALIDATION_FAILED&#187;);  }  const referralOwner = validateReferralResult.value;  const findUserResult = await this.usersService.findByEmail(email);  if (findUserResult.isErr()) {    throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);  }  if (findUserResult.value) {    throw new ConflictException(&#171;SIGN_UP_USER_ALREADY_EXISTS&#187;);  }  const createUserResult = await this.usersService.createUser({    email,    password,    adSource,    ip,    deviceId,  });  if (createUserResult.isErr()) {    if (createUserResult.error === &#171;CREATE_USER_CONFLICT&#187;) {      throw new ConflictException(&#171;SIGN_UP_USER_ALREADY_EXISTS&#187;);    }    throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);  }  const newUser = createUserResult.value;  if (referralOwner) {    const giveReferralBonusResult =      await this.bonusService.giveReferralBonus(referralOwner.id);    if (giveReferralBonusResult.isErr()) {      throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);    }    const createReferralResult = await this.referralService.createReferral(      referralOwner,      newUser,    );    if (createReferralResult.isErr()) {      throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);    }  }  if (partnerResult) {    const givePartnerRewardResult =      await this.bonusService.givePartnerReward(        partnerResult.ownerId,        partnerResult.reward,      );    if (givePartnerRewardResult.isErr()) {      throw new InternalServerErrorException(&#171;SIGN_UP_INTERNAL_ERROR&#187;);    }    const trackPartnerRewardResult =      await&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-480730","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/480730","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=480730"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/480730\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=480730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=480730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=480730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}