@hed-hog/core 0.0.14 → 0.0.17
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/auth/auth.controller.d.ts +1 -1
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +5 -4
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts +1 -1
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +2 -2
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +3 -3
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.controller.js +19 -16
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/profile/profile.service.d.ts +5 -5
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +16 -16
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/setting/setting.controller.d.ts +1 -1
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.controller.js +5 -4
- package/dist/setting/setting.controller.js.map +1 -1
- package/dist/setting/setting.service.d.ts +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +3 -3
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +1 -1
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +6 -5
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +1 -1
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +2 -2
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/setting_group.yaml +2 -0
- package/hedhog/table/setting.yaml +1 -0
- package/package.json +5 -5
- package/src/auth/auth.controller.ts +2 -1
- package/src/auth/auth.service.ts +2 -2
- package/src/profile/profile.controller.ts +6 -6
- package/src/profile/profile.service.ts +16 -17
- package/src/setting/setting.controller.ts +2 -2
- package/src/setting/setting.service.ts +3 -3
- package/src/user/user.controller.ts +15 -14
- package/src/user/user.service.ts +7 -7
|
@@ -54,13 +54,13 @@ export class ProfileController {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
@Post('totp/verify')
|
|
57
|
-
async verifyMfaTotp(@User() { id }, @Body() { token, secret, name }: VerifyMfaTotpDto) {
|
|
58
|
-
return this.profileService.verifyMfaTotp(id, token, secret, name);
|
|
57
|
+
async verifyMfaTotp(@Locale() locale: string, @User() { id }, @Body() { token, secret, name }: VerifyMfaTotpDto) {
|
|
58
|
+
return this.profileService.verifyMfaTotp(locale, id, token, secret, name);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
@Delete('totp/:mfaId/remove')
|
|
62
|
-
async removeMfaTotp(@User() { id }, @Param('mfaId', ParseIntPipe) mfaId: number, @Body() { token }: RemoveTotpMfaDto) {
|
|
63
|
-
return this.profileService.removeMfaTotp(id, mfaId, token);
|
|
62
|
+
async removeMfaTotp(@Locale() locale: string, @User() { id }, @Param('mfaId', ParseIntPipe) mfaId: number, @Body() { token }: RemoveTotpMfaDto) {
|
|
63
|
+
return this.profileService.removeMfaTotp(locale, id, mfaId, token);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
@Post('email/verify/confirm')
|
|
@@ -190,8 +190,8 @@ export class ProfileController {
|
|
|
190
190
|
|
|
191
191
|
@UseInterceptors(FileInterceptor('file'))
|
|
192
192
|
@Put('avatar')
|
|
193
|
-
updateAvatar(@User() { id }, @UploadedFile() file: MulterFile) {
|
|
194
|
-
return this.profileService.updateAvatar(id, file);
|
|
193
|
+
updateAvatar(@Locale() locale: string, @User() { id }, @UploadedFile() file: MulterFile) {
|
|
194
|
+
return this.profileService.updateAvatar(locale, id, file);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
@Put()
|
|
@@ -108,8 +108,8 @@ export class ProfileService {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
async updateAvatar(userId: number, file: MulterFile) {
|
|
112
|
-
return this.user.changeAvatar(userId, file);
|
|
111
|
+
async updateAvatar(locale: string, userId: number, file: MulterFile) {
|
|
112
|
+
return this.user.changeAvatar(locale, userId, file);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto) {
|
|
@@ -170,8 +170,7 @@ export class ProfileService {
|
|
|
170
170
|
return { token: this.jwt.sign(user) };
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
async verifyMfaTotp(userId: number, token: string, secret: string, name?: string) {
|
|
174
|
-
|
|
173
|
+
async verifyMfaTotp(locale: string, userId: number, token: string, secret: string, name?: string) {
|
|
175
174
|
const settings = await this.setting.getSettingValues([
|
|
176
175
|
'mfa-issuer',
|
|
177
176
|
'mfa-window',
|
|
@@ -193,7 +192,7 @@ export class ProfileService {
|
|
|
193
192
|
});
|
|
194
193
|
|
|
195
194
|
if (!user) {
|
|
196
|
-
throw new NotFoundException(getLocaleText('userNotFound',
|
|
195
|
+
throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found'));
|
|
197
196
|
}
|
|
198
197
|
|
|
199
198
|
const decodedSecret = Buffer.from(secret, 'base64').toString('utf-8');
|
|
@@ -207,7 +206,7 @@ export class ProfileService {
|
|
|
207
206
|
});
|
|
208
207
|
|
|
209
208
|
if (!isValid) {
|
|
210
|
-
throw new BadRequestException(getLocaleText('invalidCode',
|
|
209
|
+
throw new BadRequestException(getLocaleText('invalidCode', locale, 'Invalid code'));
|
|
211
210
|
}
|
|
212
211
|
|
|
213
212
|
if (name){
|
|
@@ -288,7 +287,7 @@ export class ProfileService {
|
|
|
288
287
|
return { otpauthUrl: secret.otpauth_url, qrCode, secret: encodedSecret };
|
|
289
288
|
}
|
|
290
289
|
|
|
291
|
-
async removeMfaTotp(userId: number, mfaId: number, token: string) {
|
|
290
|
+
async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string) {
|
|
292
291
|
try {
|
|
293
292
|
const user = await this.prisma.user.findFirst({
|
|
294
293
|
where: { id: userId },
|
|
@@ -298,7 +297,7 @@ export class ProfileService {
|
|
|
298
297
|
|
|
299
298
|
const totpMfa = user.user_mfa.find((m) => m.type === 'totp' && m.user_mfa_totp);
|
|
300
299
|
if (!totpMfa || !totpMfa.user_mfa_totp) {
|
|
301
|
-
throw new NotFoundException(getLocaleText('totpNotFound',
|
|
300
|
+
throw new NotFoundException(getLocaleText('totpNotFound', locale, 'TOTP method not found'));
|
|
302
301
|
}
|
|
303
302
|
|
|
304
303
|
const secretBuffer = totpMfa.user_mfa_totp[0].secret;
|
|
@@ -308,7 +307,7 @@ export class ProfileService {
|
|
|
308
307
|
|
|
309
308
|
const encryptedSecret = this.security.encrypt(secretBase32);
|
|
310
309
|
const encodedSecret = Buffer.from(encryptedSecret, 'utf-8').toString('base64');
|
|
311
|
-
await this.verifyMfaTotp(user.id, token, encodedSecret);
|
|
310
|
+
await this.verifyMfaTotp(locale, user.id, token, encodedSecret);
|
|
312
311
|
await this.prisma.$transaction(async (tx) => {
|
|
313
312
|
await tx.user_mfa.deleteMany({
|
|
314
313
|
where: {
|
|
@@ -354,7 +353,7 @@ export class ProfileService {
|
|
|
354
353
|
});
|
|
355
354
|
|
|
356
355
|
if (!user) {
|
|
357
|
-
throw new NotFoundException(getLocaleText('userNotFound',
|
|
356
|
+
throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found'));
|
|
358
357
|
}
|
|
359
358
|
|
|
360
359
|
let isValid = false;
|
|
@@ -493,7 +492,7 @@ export class ProfileService {
|
|
|
493
492
|
const encodedSecret = Buffer.from(encryptedSecret, 'utf-8').toString('base64');
|
|
494
493
|
|
|
495
494
|
try {
|
|
496
|
-
await this.verifyMfaTotp(userId, data.token, encodedSecret);
|
|
495
|
+
await this.verifyMfaTotp(locale, userId, data.token, encodedSecret);
|
|
497
496
|
} catch (error) {
|
|
498
497
|
throw new BadRequestException(
|
|
499
498
|
getLocaleText('invalidVerificationCode', locale, 'Invalid verification code.')
|
|
@@ -735,7 +734,7 @@ export class ProfileService {
|
|
|
735
734
|
const encodedSecret = Buffer.from(encryptedSecret, 'utf-8').toString('base64');
|
|
736
735
|
|
|
737
736
|
try {
|
|
738
|
-
await this.verifyMfaTotp(userId, data.verificationCode, encodedSecret);
|
|
737
|
+
await this.verifyMfaTotp(locale, userId, data.verificationCode, encodedSecret);
|
|
739
738
|
} catch (error) {
|
|
740
739
|
throw new BadRequestException(
|
|
741
740
|
getLocaleText('invalidVerificationCode', locale, 'Invalid verification code.')
|
|
@@ -925,7 +924,7 @@ export class ProfileService {
|
|
|
925
924
|
const encodedSecret = Buffer.from(encryptedSecret, 'utf-8').toString('base64');
|
|
926
925
|
|
|
927
926
|
try {
|
|
928
|
-
await this.verifyMfaTotp(userId, data.verificationCode, encodedSecret);
|
|
927
|
+
await this.verifyMfaTotp(locale, userId, data.verificationCode, encodedSecret);
|
|
929
928
|
} catch (error) {
|
|
930
929
|
throw new BadRequestException(
|
|
931
930
|
getLocaleText('invalidVerificationCode', locale, 'Invalid verification code.')
|
|
@@ -1221,7 +1220,7 @@ export class ProfileService {
|
|
|
1221
1220
|
}
|
|
1222
1221
|
}
|
|
1223
1222
|
|
|
1224
|
-
async generateWebAuthnAuthenticationOptionsForLogin(mfaToken: string) {
|
|
1223
|
+
async generateWebAuthnAuthenticationOptionsForLogin(locale: string, mfaToken: string) {
|
|
1225
1224
|
try {
|
|
1226
1225
|
const decoded = this.jwt.decode(mfaToken) as any;
|
|
1227
1226
|
if (!decoded || !decoded.userId) {
|
|
@@ -1239,7 +1238,7 @@ export class ProfileService {
|
|
|
1239
1238
|
});
|
|
1240
1239
|
|
|
1241
1240
|
if (!user || user.user_mfa.length === 0) {
|
|
1242
|
-
throw new NotFoundException(getLocaleText('webauthnNotConfigured',
|
|
1241
|
+
throw new NotFoundException(getLocaleText('webauthnNotConfigured', locale, 'WebAuthn not configured.'));
|
|
1243
1242
|
}
|
|
1244
1243
|
|
|
1245
1244
|
const rpID = new URL(process.env.FRONTEND_URL || 'http://localhost:3200').hostname;
|
|
@@ -1270,7 +1269,7 @@ export class ProfileService {
|
|
|
1270
1269
|
}
|
|
1271
1270
|
}
|
|
1272
1271
|
|
|
1273
|
-
async verifyWebAuthnAuthenticationForLogin(mfaToken: string, assertionResponse: any) {
|
|
1272
|
+
async verifyWebAuthnAuthenticationForLogin(locale: string, mfaToken: string, assertionResponse: any) {
|
|
1274
1273
|
try {
|
|
1275
1274
|
const decoded = this.jwt.decode(mfaToken) as any;
|
|
1276
1275
|
if (!decoded || !decoded.userId) {
|
|
@@ -1334,7 +1333,7 @@ export class ProfileService {
|
|
|
1334
1333
|
|
|
1335
1334
|
const user = await this.prisma.user.findFirst({ where: { id: userId } });
|
|
1336
1335
|
if (!user) {
|
|
1337
|
-
throw new NotFoundException(getLocaleText('userNotFound',
|
|
1336
|
+
throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found'));
|
|
1338
1337
|
}
|
|
1339
1338
|
const payload = { sub: userId };
|
|
1340
1339
|
const accessToken = await this.token.createAccessToken(payload);
|
|
@@ -90,8 +90,8 @@ export class SettingsController {
|
|
|
90
90
|
|
|
91
91
|
@UseInterceptors(FileInterceptor('file'))
|
|
92
92
|
@Post('import')
|
|
93
|
-
async importSettings(@UploadedFile() file: MulterFile) {
|
|
94
|
-
return this.settingService.importSettings(file);
|
|
93
|
+
async importSettings(@Locale() locale: string, @UploadedFile() file: MulterFile) {
|
|
94
|
+
return this.settingService.importSettings(locale, file);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
@Post('import/confirm')
|
|
@@ -49,7 +49,7 @@ export class SettingService {
|
|
|
49
49
|
return Buffer.from(compressed);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
async importSettings(file: MulterFile) {
|
|
52
|
+
async importSettings(locale: string, file: MulterFile) {
|
|
53
53
|
this.clearCache();
|
|
54
54
|
let json;
|
|
55
55
|
try {
|
|
@@ -57,11 +57,11 @@ export class SettingService {
|
|
|
57
57
|
const decompressed = pako.ungzip(file.buffer, { to: 'string' });
|
|
58
58
|
json = JSON.parse(decompressed);
|
|
59
59
|
} catch (err) {
|
|
60
|
-
throw new BadRequestException(getLocaleText('invalidCompressedFile',
|
|
60
|
+
throw new BadRequestException(getLocaleText('invalidCompressedFile', locale, 'File is not a valid .hedhog file or is corrupted.'));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
if (!Array.isArray(json)) {
|
|
64
|
-
throw new BadRequestException(getLocaleText('invalidJsonArray',
|
|
64
|
+
throw new BadRequestException(getLocaleText('invalidJsonArray', locale, 'The JSON file must contain an array of settings.'));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const slugsFromFile = json.map((item: any) => item.slug).filter(Boolean);
|
|
@@ -2,19 +2,19 @@ import { Role } from '@hed-hog/api';
|
|
|
2
2
|
import { Locale } from '@hed-hog/api-locale';
|
|
3
3
|
import { Pagination } from '@hed-hog/api-pagination';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
BadRequestException,
|
|
6
|
+
Body,
|
|
7
|
+
Controller,
|
|
8
|
+
Delete,
|
|
9
|
+
Get,
|
|
10
|
+
Inject,
|
|
11
|
+
Param,
|
|
12
|
+
ParseIntPipe,
|
|
13
|
+
Patch,
|
|
14
|
+
Post,
|
|
15
|
+
UploadedFile,
|
|
16
|
+
UseInterceptors,
|
|
17
|
+
forwardRef,
|
|
18
18
|
} from '@nestjs/common';
|
|
19
19
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
20
20
|
import { DeleteDTO } from '../dto/delete.dto';
|
|
@@ -74,10 +74,11 @@ export class UserController {
|
|
|
74
74
|
)
|
|
75
75
|
@Post(':userId/avatar')
|
|
76
76
|
async changeAvatar(
|
|
77
|
+
@Locale() locale: string,
|
|
77
78
|
@Param('userId', ParseIntPipe) userId: number,
|
|
78
79
|
@UploadedFile() avatar: MulterFile,
|
|
79
80
|
) {
|
|
80
|
-
return this.userService.changeAvatar(userId, avatar)
|
|
81
|
+
return this.userService.changeAvatar(locale, userId, avatar)
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
@Delete()
|
package/src/user/user.service.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { getLocaleText } from '@hed-hog/api-locale';
|
|
|
2
2
|
import { PaginationService } from '@hed-hog/api-pagination';
|
|
3
3
|
import { PrismaService, user } from '@hed-hog/api-prisma';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
BadRequestException,
|
|
6
|
+
Inject,
|
|
7
|
+
Injectable,
|
|
8
|
+
NotFoundException,
|
|
9
|
+
forwardRef,
|
|
10
10
|
} from '@nestjs/common';
|
|
11
11
|
import { ChallengeService } from '../challenge/challenge.service';
|
|
12
12
|
import { DeleteDTO } from '../dto/delete.dto';
|
|
@@ -64,7 +64,7 @@ export class UserService {
|
|
|
64
64
|
return this.findUserById(locale, userId);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
async changeAvatar(userId: number, avatar: MulterFile){
|
|
67
|
+
async changeAvatar(locale: string, userId: number, avatar: MulterFile){
|
|
68
68
|
const userFound = await this.prismaService.user.findUnique({
|
|
69
69
|
where: {
|
|
70
70
|
id: userId,
|
|
@@ -72,7 +72,7 @@ export class UserService {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
if (!userFound) {
|
|
75
|
-
throw new BadRequestException(getLocaleText('userNotFound',
|
|
75
|
+
throw new BadRequestException(getLocaleText('userNotFound', locale, 'User not found'));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
let photo_id = userFound.photo_id;
|