@hed-hog/core 0.0.85 → 0.0.87
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 -0
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.service.d.ts +1 -0
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +2 -1
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +10 -6
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/install/install.service.d.ts.map +1 -1
- package/dist/install/install.service.js +2 -0
- package/dist/install/install.service.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +2 -0
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +1 -0
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.service.d.ts +1 -0
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +59 -5
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts +1 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +8 -2
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +25 -1
- package/dist/task/task.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +1 -0
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.service.d.ts +2 -0
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +3 -2
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/table/user_identifier.yaml +3 -0
- package/hedhog/table/user_identifier_challenge.yaml +0 -1
- package/package.json +4 -4
- package/src/auth/auth.service.ts +2 -2
- package/src/challenge/challenge.service.ts +11 -5
- package/src/install/install.service.ts +2 -0
- package/src/oauth/oauth.service.ts +2 -0
- package/src/profile/profile.service.ts +76 -6
- package/src/setting/setting.service.ts +8 -0
- package/src/task/task.service.ts +26 -2
- package/src/user/user.service.ts +3 -2
|
@@ -28,12 +28,18 @@ export class ChallengeService {
|
|
|
28
28
|
user_id: userId,
|
|
29
29
|
type: 'email',
|
|
30
30
|
value: email,
|
|
31
|
+
verified_at: null,
|
|
31
32
|
},
|
|
32
33
|
select: { id: true }
|
|
33
34
|
});
|
|
34
35
|
|
|
36
|
+
if (!identifier) {
|
|
37
|
+
throw new NotFoundException(getLocaleText('identifierNotFound', locale, 'Email identifier not found or already verified.'));
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
const settings = await this.setting.getSettingValues([
|
|
36
|
-
'mfa-email-code-length'
|
|
41
|
+
'mfa-email-code-length',
|
|
42
|
+
'mfa-challenge-expiration-minutes'
|
|
37
43
|
]);
|
|
38
44
|
|
|
39
45
|
const user = await this.prisma.user.findUnique({
|
|
@@ -53,15 +59,15 @@ export class ChallengeService {
|
|
|
53
59
|
|
|
54
60
|
console.log({
|
|
55
61
|
hash: codeHash,
|
|
56
|
-
expires_at: new Date(Date.now() + 15 * 60000),
|
|
57
|
-
user_identifier_id: identifier
|
|
62
|
+
expires_at: new Date(Date.now() + (settings['mfa-challenge-expiration-minutes'] || 15) * 60000),
|
|
63
|
+
user_identifier_id: identifier.id,
|
|
58
64
|
})
|
|
59
65
|
|
|
60
66
|
const challenge = await this.prisma.user_identifier_challenge.create({
|
|
61
67
|
data: {
|
|
62
68
|
hash: codeHash,
|
|
63
|
-
expires_at: new Date(Date.now() + 15 * 60000),
|
|
64
|
-
user_identifier_id: identifier
|
|
69
|
+
expires_at: new Date(Date.now() + (settings['mfa-challenge-expiration-minutes'] || 15) * 60000),
|
|
70
|
+
user_identifier_id: identifier.id,
|
|
65
71
|
},
|
|
66
72
|
select: {
|
|
67
73
|
id: true,
|
|
@@ -208,6 +208,7 @@ export class InstallService {
|
|
|
208
208
|
some: {
|
|
209
209
|
type: 'email',
|
|
210
210
|
value: email,
|
|
211
|
+
enabled: true
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
}
|
|
@@ -228,6 +229,7 @@ export class InstallService {
|
|
|
228
229
|
create: {
|
|
229
230
|
type: 'email',
|
|
230
231
|
value: email,
|
|
232
|
+
enabled: true,
|
|
231
233
|
}
|
|
232
234
|
},
|
|
233
235
|
user_credential: {
|
|
@@ -128,6 +128,7 @@ export class OAuthService {
|
|
|
128
128
|
where: {
|
|
129
129
|
type: 'email',
|
|
130
130
|
value: profile.email,
|
|
131
|
+
enabled: true
|
|
131
132
|
},
|
|
132
133
|
include: { user: true },
|
|
133
134
|
});
|
|
@@ -189,6 +190,7 @@ export class OAuthService {
|
|
|
189
190
|
type: 'email',
|
|
190
191
|
value: profile.email,
|
|
191
192
|
verified_at: new Date(),
|
|
193
|
+
enabled: true
|
|
192
194
|
},
|
|
193
195
|
},
|
|
194
196
|
user_account: {
|
|
@@ -45,11 +45,34 @@ export class ProfileService {
|
|
|
45
45
|
|
|
46
46
|
async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto) {
|
|
47
47
|
|
|
48
|
+
const checkAlreadyExists = await this.prisma.user_identifier.findFirst({
|
|
49
|
+
where: { type: 'email', value: email, enabled: true},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (checkAlreadyExists) {
|
|
53
|
+
throw new BadRequestException(getLocaleText('emailAlreadyInUse', locale, 'The provided email is already in use.'));
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
const settings = await this.setting.getSettingValues([
|
|
49
57
|
'require-email-verification'
|
|
50
58
|
]);
|
|
51
59
|
|
|
52
60
|
if (settings['require-email-verification'] === true && !pin) {
|
|
61
|
+
|
|
62
|
+
const checkIfExists = await this.prisma.user_identifier.findFirst({
|
|
63
|
+
where: { type: 'email', value: email, user_id: userId },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!checkIfExists) {
|
|
67
|
+
await this.prisma.user_identifier.create({
|
|
68
|
+
data: {
|
|
69
|
+
type: 'email',
|
|
70
|
+
value: email,
|
|
71
|
+
user_id: userId,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
53
76
|
return {
|
|
54
77
|
requireEmailVerification: settings['require-email-verification'] === true
|
|
55
78
|
}
|
|
@@ -68,13 +91,33 @@ export class ProfileService {
|
|
|
68
91
|
|
|
69
92
|
if (!await this.security.validatePassword(locale, credentials, password)) {
|
|
70
93
|
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
71
|
-
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await this.prisma.$transaction( async (tx) => {
|
|
97
|
+
|
|
98
|
+
const userIdentifier = await tx.user_identifier.findFirst({
|
|
99
|
+
where: { user_id: userId, type: 'email', enabled: true },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!userIdentifier) {
|
|
103
|
+
throw new NotFoundException(getLocaleText('emailIdentifierNotFound', locale, 'Email identifier not found.'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await tx.user_identifier.updateMany({
|
|
107
|
+
where: { user_id: userId, type: 'email' },
|
|
108
|
+
data: { value: email, verified_at: null, enabled: true},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await tx.user_identifier_challenge.deleteMany({
|
|
112
|
+
where: {
|
|
113
|
+
user_identifier_id: userIdentifier.id
|
|
114
|
+
}
|
|
115
|
+
});
|
|
72
116
|
|
|
73
|
-
await this.prisma.user_identifier.updateMany({
|
|
74
|
-
where: { user_id: userId, type: 'email' },
|
|
75
|
-
data: { value: email, verified_at: null},
|
|
76
117
|
});
|
|
77
118
|
|
|
119
|
+
|
|
120
|
+
|
|
78
121
|
return {
|
|
79
122
|
requireEmailVerification: settings['require-email-verification'] === true
|
|
80
123
|
}
|
|
@@ -110,15 +153,42 @@ export class ProfileService {
|
|
|
110
153
|
|
|
111
154
|
await this.prisma.$transaction(async (tx) => {
|
|
112
155
|
|
|
156
|
+
const challenge = await tx.user_identifier_challenge.findUnique({
|
|
157
|
+
where: { id: challengeId },
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
let userIdentifierId = challenge.user_identifier_id;
|
|
161
|
+
|
|
162
|
+
if (!userIdentifierId) {
|
|
163
|
+
const userIdentifier = await tx.user_identifier.findFirst({
|
|
164
|
+
where: { user_id: userId, type: 'email' },
|
|
165
|
+
select: { id: true }
|
|
166
|
+
});
|
|
167
|
+
userIdentifierId = userIdentifier.id;
|
|
168
|
+
await tx.user_identifier_challenge.update({
|
|
169
|
+
where: { id: challengeId },
|
|
170
|
+
data: { user_identifier_id: userIdentifierId },
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
113
174
|
await tx.user_identifier_challenge.update({
|
|
114
175
|
where: { id: challengeId },
|
|
115
176
|
data: { verified_at: verifiedAt },
|
|
116
177
|
});
|
|
117
178
|
|
|
118
179
|
await tx.user_identifier.update({
|
|
119
|
-
where: { id:
|
|
180
|
+
where: { id: userIdentifierId },
|
|
120
181
|
data: {
|
|
121
182
|
verified_at: verifiedAt,
|
|
183
|
+
enabled: true
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await tx.user_identifier.deleteMany({
|
|
188
|
+
where: {
|
|
189
|
+
user_id: userId,
|
|
190
|
+
type: 'email',
|
|
191
|
+
id: { not: userIdentifierId }
|
|
122
192
|
}
|
|
123
193
|
});
|
|
124
194
|
|
|
@@ -137,7 +207,7 @@ export class ProfileService {
|
|
|
137
207
|
|
|
138
208
|
async getEmailIdentifier(userId: number) {
|
|
139
209
|
return this.prisma.user_identifier.findFirst({
|
|
140
|
-
where: { user_id: userId, type: 'email' },
|
|
210
|
+
where: { user_id: userId, type: 'email', enabled: true},
|
|
141
211
|
});
|
|
142
212
|
}
|
|
143
213
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
forwardRef,
|
|
8
8
|
Inject,
|
|
9
9
|
Injectable,
|
|
10
|
+
Logger,
|
|
10
11
|
} from '@nestjs/common';
|
|
11
12
|
import * as pako from 'pako';
|
|
12
13
|
import { CreateDTO } from './dto/create.dto';
|
|
@@ -17,6 +18,7 @@ import { UpdateDTO } from './dto/update.dto';
|
|
|
17
18
|
@Injectable()
|
|
18
19
|
export class SettingService {
|
|
19
20
|
|
|
21
|
+
private readonly logger = new Logger(SettingService.name);
|
|
20
22
|
private cachedSettings: Record<string, any> = {};
|
|
21
23
|
|
|
22
24
|
constructor(
|
|
@@ -579,6 +581,8 @@ export class SettingService {
|
|
|
579
581
|
this.cachedSettings &&
|
|
580
582
|
slugs.every(s => Object.prototype.hasOwnProperty.call(this.cachedSettings, s))
|
|
581
583
|
) {
|
|
584
|
+
this.logger.warn('Returning settings from cache');
|
|
585
|
+
|
|
582
586
|
// Retorna apenas os slugs requisitados do cache
|
|
583
587
|
const cached: Record<string, any> = {};
|
|
584
588
|
slugs.forEach(s => {
|
|
@@ -587,6 +591,8 @@ export class SettingService {
|
|
|
587
591
|
return cached;
|
|
588
592
|
}
|
|
589
593
|
|
|
594
|
+
this.logger.warn('Fetching settings from database');
|
|
595
|
+
|
|
590
596
|
slug = Array.isArray(slug) ? slug : [slug];
|
|
591
597
|
|
|
592
598
|
let setting = await this.prismaService.setting.findMany({
|
|
@@ -661,6 +667,7 @@ export class SettingService {
|
|
|
661
667
|
}
|
|
662
668
|
|
|
663
669
|
setCache(value: Record<string, any>) {
|
|
670
|
+
this.logger.warn(`Setting settings cache with ${Object.keys(value).length} items`);
|
|
664
671
|
Object.keys(value).forEach(key => {
|
|
665
672
|
this.cachedSettings[key] = value[key];
|
|
666
673
|
});
|
|
@@ -668,6 +675,7 @@ export class SettingService {
|
|
|
668
675
|
}
|
|
669
676
|
|
|
670
677
|
clearCache() {
|
|
678
|
+
this.logger.warn('Clearing settings cache');
|
|
671
679
|
this.cachedSettings = {};
|
|
672
680
|
}
|
|
673
681
|
|
package/src/task/task.service.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
1
2
|
import { Injectable, Logger } from '@nestjs/common';
|
|
2
3
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
|
3
|
-
import { PrismaService } from '@hed-hog/api-prisma';
|
|
4
4
|
|
|
5
5
|
@Injectable()
|
|
6
6
|
export class TasksService {
|
|
7
7
|
private readonly logger = new Logger(TasksService.name);
|
|
8
8
|
|
|
9
9
|
constructor(private readonly prismaService: PrismaService) {
|
|
10
|
-
|
|
10
|
+
this.clear();
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
|
|
@@ -49,6 +49,30 @@ export class TasksService {
|
|
|
49
49
|
`;
|
|
50
50
|
this.logger.verbose(`Expired sessions cleaned up in ${Date.now() - startAt}ms`);
|
|
51
51
|
this.logger.verbose('++++++++++++++++++++++++++++++++++');
|
|
52
|
+
this.logger.verbose('Starting cleanup of expired user identifier challenges');
|
|
53
|
+
startAt = Date.now();
|
|
54
|
+
await this.prismaService.$queryRaw`
|
|
55
|
+
WITH expired_challenges AS (
|
|
56
|
+
SELECT user_identifier_id
|
|
57
|
+
FROM user_identifier_challenge
|
|
58
|
+
WHERE expires_at < now() AND verified_at IS NULL
|
|
59
|
+
ORDER BY user_identifier_id
|
|
60
|
+
LIMIT 20000
|
|
61
|
+
),
|
|
62
|
+
deleted_challenges AS (
|
|
63
|
+
DELETE FROM user_identifier_challenge c
|
|
64
|
+
USING expired_challenges
|
|
65
|
+
WHERE c.user_identifier_id = expired_challenges.user_identifier_id AND c.verified_at IS NULL
|
|
66
|
+
RETURNING c.user_identifier_id
|
|
67
|
+
)
|
|
68
|
+
DELETE FROM user_identifier ui
|
|
69
|
+
USING deleted_challenges
|
|
70
|
+
WHERE ui.id = deleted_challenges.user_identifier_id
|
|
71
|
+
AND ui.verified_at IS NULL;
|
|
72
|
+
`;
|
|
73
|
+
this.logger.verbose(`Expired user identifier challenges and unverified identifiers cleaned up in ${Date.now() - startAt}ms`);
|
|
74
|
+
this.logger.verbose('++++++++++++++++++++++++++++++++++');
|
|
75
|
+
|
|
52
76
|
this.logger.verbose('Finished scheduled cleanup tasks');
|
|
53
77
|
this.logger.verbose('++++++++++++++++++++++++++++++++++');
|
|
54
78
|
}
|
package/src/user/user.service.ts
CHANGED
|
@@ -211,7 +211,7 @@ export class UserService {
|
|
|
211
211
|
const user = await tx.user.create({
|
|
212
212
|
data: {
|
|
213
213
|
name,
|
|
214
|
-
user_identifier: { create: { type: 'email', value: email } },
|
|
214
|
+
user_identifier: { create: { type: 'email', value: email, enabled: true } },
|
|
215
215
|
user_credential: { create: { type: 'password', hash: passwordHash } },
|
|
216
216
|
user_mfa: {
|
|
217
217
|
create: {
|
|
@@ -261,7 +261,8 @@ export class UserService {
|
|
|
261
261
|
user_identifier: {
|
|
262
262
|
some: {
|
|
263
263
|
type: 'email',
|
|
264
|
-
value: email.toLowerCase().trim()
|
|
264
|
+
value: email.toLowerCase().trim(),
|
|
265
|
+
enabled: true
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
268
|
},
|