@laboratory-one/api-components 0.0.17 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/cjs/auth/auth.service.d.ts +5 -0
  2. package/dist/cjs/auth/auth.service.d.ts.map +1 -1
  3. package/dist/cjs/auth/auth.service.js +14 -0
  4. package/dist/cjs/auth/auth.service.js.map +1 -1
  5. package/dist/cjs/push/push.service.d.ts +5 -0
  6. package/dist/cjs/push/push.service.d.ts.map +1 -1
  7. package/dist/cjs/push/push.service.js +12 -0
  8. package/dist/cjs/push/push.service.js.map +1 -1
  9. package/dist/cjs/user/dtos/delete-user.request.dto.d.ts +1 -1
  10. package/dist/cjs/user/dtos/delete-user.request.dto.d.ts.map +1 -1
  11. package/dist/cjs/user/dtos/delete-user.request.dto.js +1 -1
  12. package/dist/cjs/user/dtos/delete-user.request.dto.js.map +1 -1
  13. package/dist/cjs/user/dtos/index.d.ts +3 -0
  14. package/dist/cjs/user/dtos/index.d.ts.map +1 -1
  15. package/dist/cjs/user/dtos/index.js +3 -0
  16. package/dist/cjs/user/dtos/index.js.map +1 -1
  17. package/dist/cjs/user/dtos/phone-login-verify.request.dto.d.ts +6 -0
  18. package/dist/cjs/user/dtos/phone-login-verify.request.dto.d.ts.map +1 -0
  19. package/dist/cjs/user/dtos/phone-login-verify.request.dto.js +25 -0
  20. package/dist/cjs/user/dtos/phone-login-verify.request.dto.js.map +1 -0
  21. package/dist/cjs/user/dtos/phone-login.request.dto.d.ts +4 -0
  22. package/dist/cjs/user/dtos/phone-login.request.dto.d.ts.map +1 -0
  23. package/dist/cjs/user/dtos/phone-login.request.dto.js +15 -0
  24. package/dist/cjs/user/dtos/phone-login.request.dto.js.map +1 -0
  25. package/dist/cjs/user/dtos/phone-login.response.dto.d.ts +5 -0
  26. package/dist/cjs/user/dtos/phone-login.response.dto.d.ts.map +1 -0
  27. package/dist/cjs/user/dtos/phone-login.response.dto.js +20 -0
  28. package/dist/cjs/user/dtos/phone-login.response.dto.js.map +1 -0
  29. package/dist/cjs/user/dtos/update-user.request.dto.d.ts +1 -0
  30. package/dist/cjs/user/dtos/update-user.request.dto.d.ts.map +1 -1
  31. package/dist/cjs/user/dtos/update-user.request.dto.js +6 -0
  32. package/dist/cjs/user/dtos/update-user.request.dto.js.map +1 -1
  33. package/dist/cjs/user/schemas/index.d.ts +2 -0
  34. package/dist/cjs/user/schemas/index.d.ts.map +1 -1
  35. package/dist/cjs/user/schemas/index.js +2 -0
  36. package/dist/cjs/user/schemas/index.js.map +1 -1
  37. package/dist/cjs/user/schemas/phone-login-verify.schema.d.ts +15 -0
  38. package/dist/cjs/user/schemas/phone-login-verify.schema.d.ts.map +1 -0
  39. package/dist/cjs/user/schemas/phone-login-verify.schema.js +11 -0
  40. package/dist/cjs/user/schemas/phone-login-verify.schema.js.map +1 -0
  41. package/dist/cjs/user/schemas/phone-login.schema.d.ts +9 -0
  42. package/dist/cjs/user/schemas/phone-login.schema.d.ts.map +1 -0
  43. package/dist/cjs/user/schemas/phone-login.schema.js +9 -0
  44. package/dist/cjs/user/schemas/phone-login.schema.js.map +1 -0
  45. package/dist/cjs/user/schemas/update-user.schema.d.ts +3 -0
  46. package/dist/cjs/user/schemas/update-user.schema.d.ts.map +1 -1
  47. package/dist/cjs/user/schemas/update-user.schema.js +1 -0
  48. package/dist/cjs/user/schemas/update-user.schema.js.map +1 -1
  49. package/dist/cjs/user/user.controller.d.ts +6 -6
  50. package/dist/cjs/user/user.controller.d.ts.map +1 -1
  51. package/dist/cjs/user/user.controller.js +115 -15
  52. package/dist/cjs/user/user.controller.js.map +1 -1
  53. package/dist/cjs/user/user.service.d.ts +1 -0
  54. package/dist/cjs/user/user.service.d.ts.map +1 -1
  55. package/dist/cjs/user/user.service.js +6 -0
  56. package/dist/cjs/user/user.service.js.map +1 -1
  57. package/dist/cjs/utils/schema.util.d.ts +2 -0
  58. package/dist/cjs/utils/schema.util.d.ts.map +1 -1
  59. package/dist/cjs/utils/schema.util.js +2 -0
  60. package/dist/cjs/utils/schema.util.js.map +1 -1
  61. package/dist/esm/auth/auth.service.d.ts +5 -0
  62. package/dist/esm/auth/auth.service.d.ts.map +1 -1
  63. package/dist/esm/auth/auth.service.js +14 -0
  64. package/dist/esm/auth/auth.service.js.map +1 -1
  65. package/dist/esm/push/push.service.d.ts +5 -0
  66. package/dist/esm/push/push.service.d.ts.map +1 -1
  67. package/dist/esm/push/push.service.js +12 -0
  68. package/dist/esm/push/push.service.js.map +1 -1
  69. package/dist/esm/user/dtos/delete-user.request.dto.d.ts +1 -1
  70. package/dist/esm/user/dtos/delete-user.request.dto.d.ts.map +1 -1
  71. package/dist/esm/user/dtos/delete-user.request.dto.js +1 -1
  72. package/dist/esm/user/dtos/delete-user.request.dto.js.map +1 -1
  73. package/dist/esm/user/dtos/index.d.ts +3 -0
  74. package/dist/esm/user/dtos/index.d.ts.map +1 -1
  75. package/dist/esm/user/dtos/index.js +3 -0
  76. package/dist/esm/user/dtos/index.js.map +1 -1
  77. package/dist/esm/user/dtos/phone-login-verify.request.dto.d.ts +6 -0
  78. package/dist/esm/user/dtos/phone-login-verify.request.dto.d.ts.map +1 -0
  79. package/dist/esm/user/dtos/phone-login-verify.request.dto.js +21 -0
  80. package/dist/esm/user/dtos/phone-login-verify.request.dto.js.map +1 -0
  81. package/dist/esm/user/dtos/phone-login.request.dto.d.ts +4 -0
  82. package/dist/esm/user/dtos/phone-login.request.dto.d.ts.map +1 -0
  83. package/dist/esm/user/dtos/phone-login.request.dto.js +11 -0
  84. package/dist/esm/user/dtos/phone-login.request.dto.js.map +1 -0
  85. package/dist/esm/user/dtos/phone-login.response.dto.d.ts +5 -0
  86. package/dist/esm/user/dtos/phone-login.response.dto.d.ts.map +1 -0
  87. package/dist/esm/user/dtos/phone-login.response.dto.js +16 -0
  88. package/dist/esm/user/dtos/phone-login.response.dto.js.map +1 -0
  89. package/dist/esm/user/dtos/update-user.request.dto.d.ts +1 -0
  90. package/dist/esm/user/dtos/update-user.request.dto.d.ts.map +1 -1
  91. package/dist/esm/user/dtos/update-user.request.dto.js +6 -0
  92. package/dist/esm/user/dtos/update-user.request.dto.js.map +1 -1
  93. package/dist/esm/user/schemas/index.d.ts +2 -0
  94. package/dist/esm/user/schemas/index.d.ts.map +1 -1
  95. package/dist/esm/user/schemas/index.js +2 -0
  96. package/dist/esm/user/schemas/index.js.map +1 -1
  97. package/dist/esm/user/schemas/phone-login-verify.schema.d.ts +15 -0
  98. package/dist/esm/user/schemas/phone-login-verify.schema.d.ts.map +1 -0
  99. package/dist/esm/user/schemas/phone-login-verify.schema.js +8 -0
  100. package/dist/esm/user/schemas/phone-login-verify.schema.js.map +1 -0
  101. package/dist/esm/user/schemas/phone-login.schema.d.ts +9 -0
  102. package/dist/esm/user/schemas/phone-login.schema.d.ts.map +1 -0
  103. package/dist/esm/user/schemas/phone-login.schema.js +6 -0
  104. package/dist/esm/user/schemas/phone-login.schema.js.map +1 -0
  105. package/dist/esm/user/schemas/update-user.schema.d.ts +3 -0
  106. package/dist/esm/user/schemas/update-user.schema.d.ts.map +1 -1
  107. package/dist/esm/user/schemas/update-user.schema.js +1 -0
  108. package/dist/esm/user/schemas/update-user.schema.js.map +1 -1
  109. package/dist/esm/user/user.controller.d.ts +6 -6
  110. package/dist/esm/user/user.controller.d.ts.map +1 -1
  111. package/dist/esm/user/user.controller.js +107 -7
  112. package/dist/esm/user/user.controller.js.map +1 -1
  113. package/dist/esm/user/user.service.d.ts +1 -0
  114. package/dist/esm/user/user.service.d.ts.map +1 -1
  115. package/dist/esm/user/user.service.js +6 -0
  116. package/dist/esm/user/user.service.js.map +1 -1
  117. package/dist/esm/utils/schema.util.d.ts +2 -0
  118. package/dist/esm/utils/schema.util.d.ts.map +1 -1
  119. package/dist/esm/utils/schema.util.js +2 -0
  120. package/dist/esm/utils/schema.util.js.map +1 -1
  121. package/dist/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +1 -1
  123. package/src/auth/auth.service.ts +26 -0
  124. package/src/push/push.service.ts +23 -0
  125. package/src/user/dtos/delete-user.request.dto.ts +1 -1
  126. package/src/user/dtos/index.ts +3 -0
  127. package/src/user/dtos/phone-login-verify.request.dto.ts +16 -0
  128. package/src/user/dtos/phone-login.request.dto.ts +8 -0
  129. package/src/user/dtos/phone-login.response.dto.ts +12 -0
  130. package/src/user/dtos/update-user.request.dto.ts +5 -0
  131. package/src/user/schemas/index.ts +2 -0
  132. package/src/user/schemas/phone-login-verify.schema.ts +9 -0
  133. package/src/user/schemas/phone-login.schema.ts +7 -0
  134. package/src/user/schemas/update-user.schema.ts +1 -0
  135. package/src/user/user.controller.ts +142 -7
  136. package/src/user/user.service.ts +8 -0
  137. package/src/utils/schema.util.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laboratory-one/api-components",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "API components for Laboratory One",
5
5
  "author": "Laboratory One",
6
6
  "private": false,
@@ -46,6 +46,32 @@ export class AuthService {
46
46
  }
47
47
  }
48
48
 
49
+ async phoneLoginOrCreate(
50
+ phoneNumber: string,
51
+ ): Promise<stytch.OTPsSmsLoginOrCreateResponse> {
52
+ this.logger.debug('phoneLoginOrCreate');
53
+
54
+ return await this.authClient.otps.sms.loginOrCreate({
55
+ phone_number: phoneNumber,
56
+ expiration_minutes: 5,
57
+ });
58
+ }
59
+
60
+ async phoneAuthenticate({
61
+ methodId,
62
+ code,
63
+ }: {
64
+ methodId: string;
65
+ code: string;
66
+ }): Promise<stytch.OTPsAuthenticateResponse> {
67
+ this.logger.debug('phoneAuthenticate');
68
+
69
+ return await this.authClient.otps.authenticate({
70
+ method_id: methodId,
71
+ code,
72
+ });
73
+ }
74
+
49
75
  async delete(externalId: string): Promise<void> {
50
76
  this.logger.debug('delete');
51
77
 
@@ -12,6 +12,29 @@ export class PushService {
12
12
  });
13
13
  }
14
14
 
15
+ createMessage({
16
+ body,
17
+ pushToken,
18
+ data,
19
+ }: {
20
+ body: string;
21
+ pushToken: string;
22
+ data?: Record<string, unknown>;
23
+ }): ExpoPushMessage | undefined {
24
+ this.logger.debug('createMessage');
25
+
26
+ if (!Expo.isExpoPushToken(pushToken)) {
27
+ return;
28
+ }
29
+
30
+ return {
31
+ to: pushToken,
32
+ sound: 'default',
33
+ body,
34
+ data,
35
+ };
36
+ }
37
+
15
38
  createMessages({
16
39
  body,
17
40
  pushTokens,
@@ -4,5 +4,5 @@ import { ApiProperty } from '@nestjs/swagger';
4
4
  export class DeleteUserRequestDto {
5
5
  @IsString()
6
6
  @ApiProperty()
7
- email: string;
7
+ phoneNumber: string;
8
8
  }
@@ -3,3 +3,6 @@ export * from './register.request.dto';
3
3
  export * from './register.response.dto';
4
4
  export * from './update-user.request.dto';
5
5
  export * from './delete-user.request.dto';
6
+ export * from './phone-login.request.dto';
7
+ export * from './phone-login-verify.request.dto';
8
+ export * from './phone-login.response.dto';
@@ -0,0 +1,16 @@
1
+ import { IsString } from 'class-validator';
2
+ import { ApiProperty } from '@nestjs/swagger';
3
+
4
+ export class PhoneLoginVerifyRequestDto {
5
+ @IsString()
6
+ @ApiProperty()
7
+ code: string;
8
+
9
+ @IsString()
10
+ @ApiProperty()
11
+ methodId: string;
12
+
13
+ @IsString()
14
+ @ApiProperty()
15
+ phoneNumber: string;
16
+ }
@@ -0,0 +1,8 @@
1
+ import { IsString } from 'class-validator';
2
+ import { ApiProperty } from '@nestjs/swagger';
3
+
4
+ export class PhoneLoginRequestDto {
5
+ @IsString()
6
+ @ApiProperty()
7
+ phoneNumber: string;
8
+ }
@@ -0,0 +1,12 @@
1
+ import { IsString } from 'class-validator';
2
+ import { ApiProperty } from '@nestjs/swagger';
3
+
4
+ export class PhoneLoginResponseDto {
5
+ @IsString()
6
+ @ApiProperty()
7
+ methodId: string;
8
+
9
+ @IsString()
10
+ @ApiProperty()
11
+ phoneNumber: string;
12
+ }
@@ -21,4 +21,9 @@ export class UpdateUserRequestDto {
21
21
  @IsNumber()
22
22
  @ApiProperty()
23
23
  tz?: number | undefined;
24
+
25
+ @IsOptional()
26
+ @IsString()
27
+ @ApiProperty()
28
+ email?: string | undefined;
24
29
  }
@@ -1,3 +1,5 @@
1
1
  export * from './signup.schema';
2
2
  export * from './delete-user.schema';
3
3
  export * from './update-user.schema';
4
+ export * from './phone-login.schema';
5
+ export * from './phone-login-verify.schema';
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+
3
+ import { schemaPatterns } from '../../utils';
4
+
5
+ export const phoneLoginVerifySchema = z.object({
6
+ code: schemaPatterns.code,
7
+ phoneNumber: schemaPatterns.phoneNumber,
8
+ methodId: schemaPatterns.methodId,
9
+ });
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+
3
+ import { schemaPatterns } from '../../utils';
4
+
5
+ export const phoneLoginSchema = z.object({
6
+ phoneNumber: schemaPatterns.phoneNumber,
7
+ });
@@ -8,5 +8,6 @@ export const updateUserSchema = z
8
8
  fullName: schemaPatterns.fullName,
9
9
  pushToken: schemaPatterns.pushToken,
10
10
  tz: schemaPatterns.tz,
11
+ email: schemaPatterns.email,
11
12
  })
12
13
  .partial();
@@ -8,17 +8,24 @@ import {
8
8
  } from '@nestjs/common';
9
9
  import { JwtService } from '@nestjs/jwt';
10
10
  import { ApiResponse, ApiTags } from '@nestjs/swagger';
11
- import { OnboardingStatus, User } from '@prisma/client';
11
+ import { OnboardingStatus, User, UserStatus } from '@prisma/client';
12
12
  import { ExpoPushMessage } from 'expo-server-sdk';
13
13
  import * as stytch from 'stytch';
14
+ import { ZodObject } from 'zod';
14
15
 
15
- import { AuthService } from '../auth/auth.service';
16
+ import { AuthService } from '../auth';
16
17
  import { UserService } from './user.service';
17
- import { GetUserResponseDto } from './dtos/get-user.response.dto';
18
- import { UpdateUserRequestDto } from './dtos/update-user.request.dto';
19
- import { RegisterResponseDto } from './dtos/register.response.dto';
20
- import { RegisterRequestDto } from './dtos/register.request.dto';
21
- import { PushService } from '../push/push.service';
18
+ import {
19
+ GetUserResponseDto,
20
+ UpdateUserRequestDto,
21
+ RegisterResponseDto,
22
+ DeleteUserRequestDto,
23
+ PhoneLoginRequestDto,
24
+ PhoneLoginResponseDto,
25
+ PhoneLoginVerifyRequestDto,
26
+ RegisterRequestDto,
27
+ } from './dtos';
28
+ import { PushService } from '../push';
22
29
  import {
23
30
  cleanPhoneNumber,
24
31
  cleanString,
@@ -28,6 +35,11 @@ import {
28
35
  } from '../utils';
29
36
  import { AuthGuard } from '../guard';
30
37
  import * as types from '../types';
38
+ import {
39
+ deleteUserSchema,
40
+ phoneLoginSchema,
41
+ phoneLoginVerifySchema,
42
+ } from './schemas';
31
43
 
32
44
  @ApiTags('User')
33
45
  @Controller('user')
@@ -39,6 +51,89 @@ export class UserController {
39
51
  private jwtService: JwtService,
40
52
  ) {}
41
53
 
54
+ @Post('phone-login')
55
+ @ApiResponse({
56
+ status: 200,
57
+ description: 'loginOrCreate successful',
58
+ type: PhoneLoginResponseDto,
59
+ })
60
+ async phoneLogin(
61
+ @Body() dto: PhoneLoginRequestDto,
62
+ ): Promise<PhoneLoginResponseDto> {
63
+ try {
64
+ const parsedDto: PhoneLoginRequestDto = phoneLoginSchema.parse(
65
+ dto,
66
+ ) as PhoneLoginRequestDto;
67
+
68
+ const phoneNumber: string = cleanPhoneNumber(parsedDto.phoneNumber);
69
+
70
+ const user: User = await this.userService.findOneByPhoneNumber(
71
+ parsedDto.phoneNumber,
72
+ );
73
+
74
+ if (!user) {
75
+ const newUser: User = await this.userService.create({
76
+ phoneNumber,
77
+ });
78
+
79
+ if (!newUser) {
80
+ handleError('Error creating user');
81
+ }
82
+
83
+ // this is where you can do other user setup
84
+ }
85
+
86
+ const res: stytch.OTPsSmsLoginOrCreateResponse =
87
+ await this.authService.phoneLoginOrCreate(phoneNumber);
88
+
89
+ return {
90
+ methodId: res.phone_id,
91
+ phoneNumber,
92
+ };
93
+ } catch (error) {
94
+ handleError(error);
95
+ }
96
+ }
97
+
98
+ @Post('phone-login-verify')
99
+ @ApiResponse({
100
+ status: 200,
101
+ description: 'loginOrCreate successful',
102
+ type: RegisterResponseDto,
103
+ })
104
+ async phoneLoginVerify(
105
+ @Body() dto: PhoneLoginVerifyRequestDto,
106
+ ): Promise<RegisterResponseDto> {
107
+ try {
108
+ const parsedDto: PhoneLoginVerifyRequestDto =
109
+ phoneLoginVerifySchema.parse(dto) as PhoneLoginVerifyRequestDto;
110
+
111
+ const res: stytch.OTPsAuthenticateResponse =
112
+ await this.authService.phoneAuthenticate({
113
+ methodId: parsedDto.methodId,
114
+ code: parsedDto.code,
115
+ });
116
+
117
+ const user: User = await this.userService.findOneByPhoneNumber(
118
+ parsedDto.phoneNumber,
119
+ );
120
+
121
+ await this.userService.update(user.id, {
122
+ externalId: res.user_id,
123
+ });
124
+
125
+ const payload = { userId: user.id };
126
+ const token: string = await this.jwtService.signAsync(payload);
127
+
128
+ return {
129
+ ...user,
130
+ token,
131
+ };
132
+ } catch (error) {
133
+ handleError(error);
134
+ }
135
+ }
136
+
42
137
  @Post('signup')
43
138
  @ApiResponse({
44
139
  status: 200,
@@ -59,6 +154,7 @@ export class UserController {
59
154
  }
60
155
 
61
156
  const user: User = await this.userService.create({
157
+ phoneNumber: res.user_id, // use phone number as placeholder
62
158
  email: cleanedDTO.email,
63
159
  externalId: res.user_id,
64
160
  });
@@ -191,4 +287,43 @@ export class UserController {
191
287
  handleError(error);
192
288
  }
193
289
  }
290
+
291
+ @UseGuards(AuthGuard)
292
+ @Post('delete')
293
+ @ApiResponse({
294
+ status: 200,
295
+ description: 'delete user successful',
296
+ type: GetUserResponseDto,
297
+ })
298
+ async delete(
299
+ @Request() req: types.reqType,
300
+ @Body() dto: DeleteUserRequestDto,
301
+ ): Promise<void> {
302
+ try {
303
+ const parsedDto: DeleteUserRequestDto = (
304
+ deleteUserSchema as ZodObject<any>
305
+ ).parse(dto) as DeleteUserRequestDto;
306
+
307
+ const user: User = await this.userService.findOneByUserId(
308
+ req.user.userId,
309
+ );
310
+
311
+ if (!user) {
312
+ handleError('User not found');
313
+ }
314
+
315
+ if (parsedDto.phoneNumber !== user.phoneNumber) {
316
+ handleError('Invalid email');
317
+ }
318
+
319
+ await this.authService.delete(user.externalId);
320
+ await this.userService.update(req.user.userId, {
321
+ status: UserStatus.DELETED,
322
+ email: user.externalId,
323
+ phoneNumber: user.externalId,
324
+ });
325
+ } catch (error) {
326
+ handleError(error);
327
+ }
328
+ }
194
329
  }
@@ -25,6 +25,14 @@ export class UserService {
25
25
  });
26
26
  }
27
27
 
28
+ async findOneByPhoneNumber(phoneNumber: string): Promise<User> {
29
+ this.logger.debug('findOneByPhoneNumber');
30
+
31
+ return this.prisma.user.findUnique({
32
+ where: { phoneNumber },
33
+ });
34
+ }
35
+
28
36
  async findOneByUserId(userId: string): Promise<User> {
29
37
  this.logger.debug('findOneByUserId');
30
38
 
@@ -8,4 +8,6 @@ export const schemaPatterns = {
8
8
  pushToken: z.string().min(1).max(500).trim(),
9
9
  tz: z.number().int().min(-10000).max(10000),
10
10
  date: z.string().date(),
11
+ code: z.string().length(6).trim(),
12
+ methodId: z.string().min(1).max(500).trim(),
11
13
  };