nicot-simple-user 2.0.0 → 2.1.0

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 (39) hide show
  1. package/dist/simple-user/i18n/i18n-dict.d.ts +2 -0
  2. package/dist/simple-user/i18n/i18n-dict.js +157 -0
  3. package/dist/simple-user/i18n/i18n-dict.js.map +1 -0
  4. package/dist/simple-user/i18n/i18n-init.d.ts +8 -0
  5. package/dist/simple-user/i18n/i18n-init.js +14 -0
  6. package/dist/simple-user/i18n/i18n-init.js.map +1 -0
  7. package/dist/simple-user/i18n/i18n-middleware.d.ts +1 -0
  8. package/dist/simple-user/i18n/i18n-middleware.js +8 -0
  9. package/dist/simple-user/i18n/i18n-middleware.js.map +1 -0
  10. package/dist/simple-user/i18n/i18n-setup.service.d.ts +4 -0
  11. package/dist/simple-user/i18n/i18n-setup.service.js +26 -0
  12. package/dist/simple-user/i18n/i18n-setup.service.js.map +1 -0
  13. package/dist/simple-user/i18n/index.d.ts +3 -0
  14. package/dist/simple-user/i18n/index.js +20 -0
  15. package/dist/simple-user/i18n/index.js.map +1 -0
  16. package/dist/simple-user/index.d.ts +1 -0
  17. package/dist/simple-user/index.js +3 -0
  18. package/dist/simple-user/index.js.map +1 -1
  19. package/dist/simple-user/login/login.controller.js +2 -0
  20. package/dist/simple-user/login/login.controller.js.map +1 -1
  21. package/dist/simple-user/options.d.ts +1 -0
  22. package/dist/simple-user/send-code/send-code.controller.js +2 -0
  23. package/dist/simple-user/send-code/send-code.controller.js.map +1 -1
  24. package/dist/simple-user/send-code/send-code.service.js +3 -3
  25. package/dist/simple-user/send-code/send-code.service.js.map +1 -1
  26. package/dist/simple-user/simple-user/contact.dto.js.map +1 -1
  27. package/dist/simple-user/simple-user/simple-user.service.js +5 -5
  28. package/dist/simple-user/simple-user/simple-user.service.js.map +1 -1
  29. package/dist/simple-user/simple-user-initial-creation/simple-user-initial-creation.service.js +1 -1
  30. package/dist/simple-user/simple-user-initial-creation/simple-user-initial-creation.service.js.map +1 -1
  31. package/dist/simple-user/simple-user.module.js +13 -2
  32. package/dist/simple-user/simple-user.module.js.map +1 -1
  33. package/dist/simple-user/user-center/user-center.controller.js +2 -0
  34. package/dist/simple-user/user-center/user-center.controller.js.map +1 -1
  35. package/dist/tsconfig.build.tsbuildinfo +1 -1
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +12 -7
  38. package/test/app-e2e-v2.spec.ts +7 -11
  39. package/test/i18n.spec.ts +285 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nicot-simple-user",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Simple user module based on NICOT",
5
5
  "author": "Nanahira <nanahira@momobako.com>",
6
6
  "license": "MIT",
@@ -38,11 +38,11 @@
38
38
  "@nestjs/common": "^11.0.1",
39
39
  "@nestjs/core": "^11.0.1",
40
40
  "@nestjs/swagger": "^11.2.3",
41
- "@nestjs/typeorm": "^11.0.0",
41
+ "@nestjs/typeorm": "^11.0.1",
42
42
  "class-transformer": "^0.5.1",
43
- "class-validator": "^0.14.3",
44
- "nicot": "^1.3.4",
45
- "typeorm": "^0.3.28"
43
+ "class-validator": "^0.14.0 || ^0.15.0",
44
+ "nicot": "^1.4.0",
45
+ "typeorm": "^0.3.27 || ^1.0.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@eslint/eslintrc": "^3.2.0",
@@ -52,18 +52,21 @@
52
52
  "@nestjs/platform-express": "^11.0.1",
53
53
  "@nestjs/schematics": "^11.0.0",
54
54
  "@nestjs/testing": "^11.0.1",
55
+ "@nestjs/typeorm": "^11.0.1",
55
56
  "@swc/cli": "^0.6.0",
56
57
  "@swc/core": "^1.10.7",
57
58
  "@types/express": "^5.0.6",
58
59
  "@types/jest": "^29.5.14",
59
60
  "@types/node": "^22.10.7",
60
61
  "@types/supertest": "^6.0.2",
62
+ "class-validator": "^0.15.1",
61
63
  "eslint": "^9.18.0",
62
64
  "eslint-config-prettier": "^10.0.1",
63
65
  "eslint-plugin-prettier": "^5.2.2",
64
66
  "globals": "^16.0.0",
65
67
  "jest": "^29.7.0",
66
- "nesties": "^1.1.29",
68
+ "nesties": "^1.1.34",
69
+ "nicot": "^1.4.0",
67
70
  "pg": "^8.16.3",
68
71
  "prettier": "^3.4.2",
69
72
  "reflect-metadata": "^0.2.2",
@@ -74,6 +77,7 @@
74
77
  "ts-loader": "^9.5.2",
75
78
  "ts-node": "^10.9.2",
76
79
  "tsconfig-paths": "^4.2.0",
80
+ "typeorm": "^1.0.0",
77
81
  "typescript": "^5.7.3",
78
82
  "typescript-eslint": "^8.20.0",
79
83
  "yaml": "^2.8.2"
@@ -93,6 +97,7 @@
93
97
  "**/*.(t|j)s"
94
98
  ],
95
99
  "coverageDirectory": "../coverage",
96
- "testEnvironment": "node"
100
+ "testEnvironment": "node",
101
+ "maxWorkers": 1
97
102
  }
98
103
  }
@@ -265,8 +265,7 @@ describe('SimpleUserModule v2 - mobile support (e2e)', () => {
265
265
  .expect(200);
266
266
 
267
267
  expectOkEnvelope(res.body);
268
- const data = (res.body as GenericReturnMessageDto<LoginResponseDto>)
269
- .data;
268
+ const data = (res.body as GenericReturnMessageDto<LoginResponseDto>).data;
270
269
  expect(data.token).toHaveLength(64);
271
270
  mobileToken = data.token;
272
271
  });
@@ -314,9 +313,8 @@ describe('SimpleUserModule v2 - mobile support (e2e)', () => {
314
313
  .expect(200);
315
314
 
316
315
  expectOkEnvelope(res.body);
317
- mobileToken = (
318
- res.body as GenericReturnMessageDto<LoginResponseDto>
319
- ).data.token;
316
+ mobileToken = (res.body as GenericReturnMessageDto<LoginResponseDto>).data
317
+ .token;
320
318
  });
321
319
  });
322
320
 
@@ -523,9 +521,8 @@ describe('SimpleUserModule v2 - mobile support (e2e)', () => {
523
521
  .expect(200);
524
522
 
525
523
  expectOkEnvelope(res.body);
526
- token = (
527
- res.body as GenericReturnMessageDto<LoginResponseDto>
528
- ).data.token;
524
+ token = (res.body as GenericReturnMessageDto<LoginResponseDto>).data
525
+ .token;
529
526
  });
530
527
  });
531
528
 
@@ -575,9 +572,8 @@ describe('SimpleUserModule v2 - mobile support (e2e)', () => {
575
572
  .expect(200);
576
573
 
577
574
  expectOkEnvelope(res.body);
578
- token = (
579
- res.body as GenericReturnMessageDto<LoginResponseDto>
580
- ).data.token;
575
+ token = (res.body as GenericReturnMessageDto<LoginResponseDto>).data
576
+ .token;
581
577
  });
582
578
  });
583
579
 
@@ -0,0 +1,285 @@
1
+ import { Test, TestingModule } from '@nestjs/testing';
2
+ import { INestApplication } from '@nestjs/common';
3
+ import request from 'supertest';
4
+ import { AppModule } from './../src/app.module';
5
+ import { GenericReturnMessageDto } from 'nicot';
6
+ import { LoginResponseDto } from '../src/simple-user/simple-user/login.dto';
7
+ import { SIMPLE_USER_I18N_DICT } from '../src/simple-user/i18n/i18n-dict';
8
+
9
+ const dict = SIMPLE_USER_I18N_DICT;
10
+
11
+ describe('i18n (e2e)', () => {
12
+ let app: INestApplication;
13
+ let httpServer: any;
14
+
15
+ const EMAIL_CODE = '123456';
16
+ const SMS_CODE = '654321';
17
+
18
+ const rand = Math.random().toString(16).slice(2);
19
+ const email = `i18n_${rand}@example.com`;
20
+ const mobile = `86 1380000${rand.slice(0, 4)}`;
21
+ const ssaid = `ssaid_i18n_${rand}`;
22
+ const password = `P@ss_${rand}`;
23
+
24
+ let token: string;
25
+
26
+ beforeAll(async () => {
27
+ const moduleFixture: TestingModule = await Test.createTestingModule({
28
+ imports: [AppModule],
29
+ }).compile();
30
+
31
+ app = moduleFixture.createNestApplication();
32
+ await app.init();
33
+ httpServer = app.getHttpServer();
34
+
35
+ await request(httpServer)
36
+ .post('/send-code/send')
37
+ .set('x-client-ssaid', ssaid)
38
+ .send({ email, codePurpose: 'Login' });
39
+
40
+ const res = await request(httpServer)
41
+ .post('/login')
42
+ .set('x-client-ssaid', ssaid)
43
+ .send({ email, code: EMAIL_CODE, setPassword: password });
44
+
45
+ token = (res.body as GenericReturnMessageDto<LoginResponseDto>).data.token;
46
+ });
47
+
48
+ afterAll(async () => {
49
+ await app.close();
50
+ });
51
+
52
+ const lang = (req: request.Test, locale?: string) =>
53
+ locale ? req.set('x-client-language', locale) : req;
54
+
55
+ // ──────────────────────────────────────────────────────────
56
+ // 403 Invalid password
57
+ // ──────────────────────────────────────────────────────────
58
+
59
+ describe('403 - invalid_password', () => {
60
+ const makeRequest = (locale?: string) =>
61
+ lang(
62
+ request(httpServer)
63
+ .post('/login')
64
+ .set('x-client-ssaid', ssaid)
65
+ .send({ email, password: 'WRONG_PASSWORD' }),
66
+ locale,
67
+ );
68
+
69
+ it('default (en)', async () => {
70
+ const res = await makeRequest().expect(403);
71
+ expect(res.body.message).toBe(dict.en.invalid_password);
72
+ });
73
+
74
+ it('zh', async () => {
75
+ const res = await makeRequest('zh').expect(403);
76
+ expect(res.body.message).toBe(dict.zh.invalid_password);
77
+ });
78
+
79
+ it('zh-Hant', async () => {
80
+ const res = await makeRequest('zh-Hant').expect(403);
81
+ expect(res.body.message).toBe(dict['zh-Hant'].invalid_password);
82
+ });
83
+
84
+ it('ja', async () => {
85
+ const res = await makeRequest('ja').expect(403);
86
+ expect(res.body.message).toBe(dict.ja.invalid_password);
87
+ });
88
+
89
+ it('ko', async () => {
90
+ const res = await makeRequest('ko').expect(403);
91
+ expect(res.body.message).toBe(dict.ko.invalid_password);
92
+ });
93
+
94
+ it('unknown locale falls back to en', async () => {
95
+ const res = await makeRequest('xx-YY').expect(403);
96
+ expect(res.body.message).toBe(dict.en.invalid_password);
97
+ });
98
+ });
99
+
100
+ // ──────────────────────────────────────────────────────────
101
+ // 403 Invalid verification code
102
+ // ──────────────────────────────────────────────────────────
103
+
104
+ describe('403 - invalid_code', () => {
105
+ beforeAll(async () => {
106
+ await request(httpServer)
107
+ .post('/send-code/send')
108
+ .set('x-client-ssaid', ssaid)
109
+ .send({ email, codePurpose: 'Login' });
110
+ });
111
+
112
+ const makeRequest = (locale?: string) =>
113
+ lang(
114
+ request(httpServer)
115
+ .get('/send-code/verify')
116
+ .query({ email, codePurpose: 'Login', code: '000000' }),
117
+ locale,
118
+ );
119
+
120
+ it('default (en)', async () => {
121
+ const res = await makeRequest().expect(403);
122
+ expect(res.body.message).toBe(dict.en.invalid_code);
123
+ });
124
+
125
+ it('zh', async () => {
126
+ const res = await makeRequest('zh').expect(403);
127
+ expect(res.body.message).toBe(dict.zh.invalid_code);
128
+ });
129
+
130
+ it('fr', async () => {
131
+ const res = await makeRequest('fr').expect(403);
132
+ expect(res.body.message).toBe(dict.fr.invalid_code);
133
+ });
134
+
135
+ it('de', async () => {
136
+ const res = await makeRequest('de').expect(403);
137
+ expect(res.body.message).toBe(dict.de.invalid_code);
138
+ });
139
+ });
140
+
141
+ // ──────────────────────────────────────────────────────────
142
+ // 404 User does not exist (password login for unknown user)
143
+ // ──────────────────────────────────────────────────────────
144
+
145
+ describe('404 - user_not_exist_provide_code', () => {
146
+ const nonExistentEmail = `nouser_${rand}@example.com`;
147
+
148
+ const makeRequest = (locale?: string) =>
149
+ lang(
150
+ request(httpServer)
151
+ .post('/login')
152
+ .set('x-client-ssaid', `ssaid_nouser_${rand}`)
153
+ .send({ email: nonExistentEmail, password: 'any' }),
154
+ locale,
155
+ );
156
+
157
+ it('default (en)', async () => {
158
+ const res = await makeRequest().expect(404);
159
+ expect(res.body.message).toBe(dict.en.user_not_exist_provide_code);
160
+ });
161
+
162
+ it('zh', async () => {
163
+ const res = await makeRequest('zh').expect(404);
164
+ expect(res.body.message).toBe(dict.zh.user_not_exist_provide_code);
165
+ });
166
+
167
+ it('es', async () => {
168
+ const res = await makeRequest('es').expect(404);
169
+ expect(res.body.message).toBe(dict.es.user_not_exist_provide_code);
170
+ });
171
+
172
+ it('ru', async () => {
173
+ const res = await makeRequest('ru').expect(404);
174
+ expect(res.body.message).toBe(dict.ru.user_not_exist_provide_code);
175
+ });
176
+ });
177
+
178
+ // ──────────────────────────────────────────────────────────
179
+ // 403 Current password is incorrect
180
+ // ──────────────────────────────────────────────────────────
181
+
182
+ describe('403 - current_password_incorrect', () => {
183
+ const makeRequest = (locale?: string) =>
184
+ lang(
185
+ request(httpServer)
186
+ .post('/user-center/change-password')
187
+ .set('x-client-ssaid', ssaid)
188
+ .set('x-client-token', token)
189
+ .send({ currentPassword: 'WRONG', newPassword: 'Whatever1!' }),
190
+ locale,
191
+ );
192
+
193
+ it('default (en)', async () => {
194
+ const res = await makeRequest().expect(403);
195
+ expect(res.body.message).toBe(dict.en.current_password_incorrect);
196
+ });
197
+
198
+ it('zh', async () => {
199
+ const res = await makeRequest('zh').expect(403);
200
+ expect(res.body.message).toBe(dict.zh.current_password_incorrect);
201
+ });
202
+
203
+ it('zh-Hant', async () => {
204
+ const res = await makeRequest('zh-Hant').expect(403);
205
+ expect(res.body.message).toBe(dict['zh-Hant'].current_password_incorrect);
206
+ });
207
+
208
+ it('tr', async () => {
209
+ const res = await makeRequest('tr').expect(403);
210
+ expect(res.body.message).toBe(dict.tr.current_password_incorrect);
211
+ });
212
+ });
213
+
214
+ // ──────────────────────────────────────────────────────────
215
+ // 200 success messages should NOT be translated
216
+ // ──────────────────────────────────────────────────────────
217
+
218
+ describe('200 success is not affected by i18n', () => {
219
+ it('GET /user-center/me with zh should still return success', async () => {
220
+ const res = await request(httpServer)
221
+ .get('/user-center/me')
222
+ .set('x-client-ssaid', ssaid)
223
+ .set('x-client-token', token)
224
+ .set('x-client-language', 'zh')
225
+ .expect(200);
226
+
227
+ expect(res.body.message).toBe('success');
228
+ });
229
+ });
230
+
231
+ // ──────────────────────────────────────────────────────────
232
+ // Hierarchy fallback
233
+ // ──────────────────────────────────────────────────────────
234
+
235
+ describe('hierarchy fallback', () => {
236
+ const makeRequest = (locale: string) =>
237
+ request(httpServer)
238
+ .post('/login')
239
+ .set('x-client-ssaid', ssaid)
240
+ .set('x-client-language', locale)
241
+ .send({ email, password: 'WRONG_PASSWORD' });
242
+
243
+ it('zh-Hans-CN falls back to zh', async () => {
244
+ const res = await makeRequest('zh-Hans-CN').expect(403);
245
+ expect(res.body.message).toBe(dict.zh.invalid_password);
246
+ });
247
+
248
+ it('zh-TW falls back to zh', async () => {
249
+ const res = await makeRequest('zh-TW').expect(403);
250
+ expect(res.body.message).toBe(dict.zh.invalid_password);
251
+ });
252
+ });
253
+
254
+ // ──────────────────────────────────────────────────────────
255
+ // Mobile errors also translated
256
+ // ──────────────────────────────────────────────────────────
257
+
258
+ describe('mobile errors are also i18n-translated', () => {
259
+ it('404 via mobile login (zh)', async () => {
260
+ const res = await request(httpServer)
261
+ .post('/login')
262
+ .set('x-client-ssaid', `ssaid_mob_${rand}`)
263
+ .set('x-client-language', 'zh')
264
+ .send({ mobile: '86 19900000000', password: 'any' })
265
+ .expect(404);
266
+
267
+ expect(res.body.message).toBe(dict.zh.user_not_exist_provide_code);
268
+ });
269
+
270
+ it('403 invalid SMS code (ja)', async () => {
271
+ await request(httpServer)
272
+ .post('/send-code/send')
273
+ .set('x-client-ssaid', ssaid)
274
+ .send({ mobile, codePurpose: 'Login' });
275
+
276
+ const res = await request(httpServer)
277
+ .get('/send-code/verify')
278
+ .query({ mobile, codePurpose: 'Login', code: '000000' })
279
+ .set('x-client-language', 'ja')
280
+ .expect(403);
281
+
282
+ expect(res.body.message).toBe(dict.ja.invalid_code);
283
+ });
284
+ });
285
+ });