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.
- package/dist/simple-user/i18n/i18n-dict.d.ts +2 -0
- package/dist/simple-user/i18n/i18n-dict.js +157 -0
- package/dist/simple-user/i18n/i18n-dict.js.map +1 -0
- package/dist/simple-user/i18n/i18n-init.d.ts +8 -0
- package/dist/simple-user/i18n/i18n-init.js +14 -0
- package/dist/simple-user/i18n/i18n-init.js.map +1 -0
- package/dist/simple-user/i18n/i18n-middleware.d.ts +1 -0
- package/dist/simple-user/i18n/i18n-middleware.js +8 -0
- package/dist/simple-user/i18n/i18n-middleware.js.map +1 -0
- package/dist/simple-user/i18n/i18n-setup.service.d.ts +4 -0
- package/dist/simple-user/i18n/i18n-setup.service.js +26 -0
- package/dist/simple-user/i18n/i18n-setup.service.js.map +1 -0
- package/dist/simple-user/i18n/index.d.ts +3 -0
- package/dist/simple-user/i18n/index.js +20 -0
- package/dist/simple-user/i18n/index.js.map +1 -0
- package/dist/simple-user/index.d.ts +1 -0
- package/dist/simple-user/index.js +3 -0
- package/dist/simple-user/index.js.map +1 -1
- package/dist/simple-user/login/login.controller.js +2 -0
- package/dist/simple-user/login/login.controller.js.map +1 -1
- package/dist/simple-user/options.d.ts +1 -0
- package/dist/simple-user/send-code/send-code.controller.js +2 -0
- package/dist/simple-user/send-code/send-code.controller.js.map +1 -1
- package/dist/simple-user/send-code/send-code.service.js +3 -3
- package/dist/simple-user/send-code/send-code.service.js.map +1 -1
- package/dist/simple-user/simple-user/contact.dto.js.map +1 -1
- package/dist/simple-user/simple-user/simple-user.service.js +5 -5
- package/dist/simple-user/simple-user/simple-user.service.js.map +1 -1
- package/dist/simple-user/simple-user-initial-creation/simple-user-initial-creation.service.js +1 -1
- package/dist/simple-user/simple-user-initial-creation/simple-user-initial-creation.service.js.map +1 -1
- package/dist/simple-user/simple-user.module.js +13 -2
- package/dist/simple-user/simple-user.module.js.map +1 -1
- package/dist/simple-user/user-center/user-center.controller.js +2 -0
- package/dist/simple-user/user-center/user-center.controller.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -7
- package/test/app-e2e-v2.spec.ts +7 -11
- 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.
|
|
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.
|
|
41
|
+
"@nestjs/typeorm": "^11.0.1",
|
|
42
42
|
"class-transformer": "^0.5.1",
|
|
43
|
-
"class-validator": "^0.14.
|
|
44
|
-
"nicot": "^1.
|
|
45
|
-
"typeorm": "^0.3.
|
|
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.
|
|
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
|
}
|
package/test/app-e2e-v2.spec.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
});
|