@hed-hog/core 0.0.6 → 0.0.10

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 (78) hide show
  1. package/dist/auth/auth.service.d.ts +1 -4
  2. package/dist/auth/auth.service.d.ts.map +1 -1
  3. package/dist/auth/auth.service.js +9 -19
  4. package/dist/auth/auth.service.js.map +1 -1
  5. package/dist/challenge/challenge.service.d.ts +1 -4
  6. package/dist/challenge/challenge.service.d.ts.map +1 -1
  7. package/dist/challenge/challenge.service.js +26 -16
  8. package/dist/challenge/challenge.service.js.map +1 -1
  9. package/dist/file/file.service.d.ts +2 -5
  10. package/dist/file/file.service.d.ts.map +1 -1
  11. package/dist/file/file.service.js +40 -16
  12. package/dist/file/file.service.js.map +1 -1
  13. package/dist/mail/mail.service.d.ts +1 -4
  14. package/dist/mail/mail.service.d.ts.map +1 -1
  15. package/dist/mail/mail.service.js +10 -9
  16. package/dist/mail/mail.service.js.map +1 -1
  17. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  18. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  19. package/dist/oauth/oauth.service.d.ts +2 -5
  20. package/dist/oauth/oauth.service.d.ts.map +1 -1
  21. package/dist/oauth/oauth.service.js +12 -15
  22. package/dist/oauth/oauth.service.js.map +1 -1
  23. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  24. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  25. package/dist/oauth/providers/facebook.provider.d.ts +2 -5
  26. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  27. package/dist/oauth/providers/facebook.provider.js +18 -15
  28. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  29. package/dist/oauth/providers/github.provider.d.ts +2 -5
  30. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  31. package/dist/oauth/providers/github.provider.js +17 -14
  32. package/dist/oauth/providers/github.provider.js.map +1 -1
  33. package/dist/oauth/providers/google.provider.d.ts +2 -5
  34. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  35. package/dist/oauth/providers/google.provider.js +18 -15
  36. package/dist/oauth/providers/google.provider.js.map +1 -1
  37. package/dist/oauth/providers/microsoft.provider.d.ts +2 -5
  38. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  39. package/dist/oauth/providers/microsoft.provider.js +17 -14
  40. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  41. package/dist/profile/profile.service.d.ts +0 -2
  42. package/dist/profile/profile.service.d.ts.map +1 -1
  43. package/dist/profile/profile.service.js +41 -20
  44. package/dist/profile/profile.service.js.map +1 -1
  45. package/dist/session/session.service.d.ts +1 -4
  46. package/dist/session/session.service.d.ts.map +1 -1
  47. package/dist/session/session.service.js +4 -7
  48. package/dist/session/session.service.js.map +1 -1
  49. package/dist/setting/setting.controller.d.ts +3 -4
  50. package/dist/setting/setting.controller.d.ts.map +1 -1
  51. package/dist/setting/setting.controller.js +7 -3
  52. package/dist/setting/setting.controller.js.map +1 -1
  53. package/dist/setting/setting.service.d.ts +2 -4
  54. package/dist/setting/setting.service.d.ts.map +1 -1
  55. package/dist/setting/setting.service.js +45 -5
  56. package/dist/setting/setting.service.js.map +1 -1
  57. package/dist/token/token.service.d.ts +1 -4
  58. package/dist/token/token.service.d.ts.map +1 -1
  59. package/dist/token/token.service.js +8 -11
  60. package/dist/token/token.service.js.map +1 -1
  61. package/hedhog/data/setting_group.yaml +11 -20
  62. package/package.json +7 -5
  63. package/src/auth/auth.service.ts +11 -22
  64. package/src/challenge/challenge.service.ts +35 -19
  65. package/src/file/file.service.ts +45 -20
  66. package/src/mail/mail.service.ts +16 -12
  67. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  68. package/src/oauth/oauth.service.ts +16 -19
  69. package/src/oauth/providers/abstract.provider.ts +1 -1
  70. package/src/oauth/providers/facebook.provider.ts +22 -18
  71. package/src/oauth/providers/github.provider.ts +22 -18
  72. package/src/oauth/providers/google.provider.ts +23 -17
  73. package/src/oauth/providers/microsoft.provider.ts +23 -18
  74. package/src/profile/profile.service.ts +52 -27
  75. package/src/session/session.service.ts +6 -11
  76. package/src/setting/setting.controller.ts +9 -3
  77. package/src/setting/setting.service.ts +18 -9
  78. package/src/token/token.service.ts +12 -15
@@ -3,14 +3,14 @@ import { PrismaService, user } from '@hed-hog/api-prisma';
3
3
  import { BadRequestException, forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
4
4
  import { JwtService } from '@nestjs/jwt';
5
5
  import type {
6
- VerifiedAuthenticationResponse,
7
- VerifiedRegistrationResponse,
6
+ VerifiedAuthenticationResponse,
7
+ VerifiedRegistrationResponse,
8
8
  } from '@simplewebauthn/server';
9
9
  import {
10
- generateAuthenticationOptions,
11
- generateRegistrationOptions,
12
- verifyAuthenticationResponse,
13
- verifyRegistrationResponse,
10
+ generateAuthenticationOptions,
11
+ generateRegistrationOptions,
12
+ verifyAuthenticationResponse,
13
+ verifyRegistrationResponse,
14
14
  } from '@simplewebauthn/server';
15
15
  import * as qrcode from 'qrcode';
16
16
  import * as speakeasy from 'speakeasy';
@@ -27,7 +27,6 @@ import { UpdateDto } from './dto/update.dto';
27
27
 
28
28
  @Injectable()
29
29
  export class ProfileService {
30
- public settings: Record<string, any> = {};
31
30
 
32
31
  constructor(
33
32
  private readonly prisma: PrismaService,
@@ -53,21 +52,6 @@ export class ProfileService {
53
52
  return { success: true };
54
53
  }
55
54
 
56
- async onModuleInit() {
57
- this.settings = await this.setting.getSettingValues([
58
- 'mfa-issuer',
59
- 'mfa-window',
60
- 'mfa-setp',
61
- 'system-name',
62
- 'google_client_id',
63
- 'google_client_secret',
64
- 'google_scopes',
65
- 'url',
66
- 'mfa-challenge-expiration-minutes',
67
- 'access-token-expiration-minutes'
68
- ]);
69
- }
70
-
71
55
  async confirmEmailVerification(locale: string, userId: number, { pin, hash, name, email }: EmailVerificationDto) {
72
56
  const challenge = await this.challenge.verifyCode(locale, userId, pin, hash, name, email)
73
57
  const updatedUser = await this.prisma.user.findFirst({ where: { id: userId }})
@@ -187,8 +171,22 @@ export class ProfileService {
187
171
  }
188
172
 
189
173
  async verifyMfaTotp(userId: number, token: string, secret: string, name?: string) {
190
- const window = this.settings['mfa-window'] ?? 0;
191
- const step = this.settings['mfa-setp'] ?? 30;
174
+
175
+ const settings = await this.setting.getSettingValues([
176
+ 'mfa-issuer',
177
+ 'mfa-window',
178
+ 'mfa-setp',
179
+ 'system-name',
180
+ 'google_client_id',
181
+ 'google_client_secret',
182
+ 'google_scopes',
183
+ 'url',
184
+ 'mfa-challenge-expiration-minutes',
185
+ 'access-token-expiration-minutes'
186
+ ]);
187
+
188
+ const window = settings['mfa-window'] ?? 0;
189
+ const step = settings['mfa-setp'] ?? 30;
192
190
  const user = await this.prisma.user.findFirst({
193
191
  where: { id: userId },
194
192
  include: { user_mfa: { include: { user_mfa_totp: true }}}
@@ -242,6 +240,20 @@ export class ProfileService {
242
240
  }
243
241
 
244
242
  async generateMfaTotp(locale: string, userId: number) {
243
+
244
+ const settings = await this.setting.getSettingValues([
245
+ 'mfa-issuer',
246
+ 'mfa-window',
247
+ 'mfa-setp',
248
+ 'system-name',
249
+ 'google_client_id',
250
+ 'google_client_secret',
251
+ 'google_scopes',
252
+ 'url',
253
+ 'mfa-challenge-expiration-minutes',
254
+ 'access-token-expiration-minutes'
255
+ ]);
256
+
245
257
  const user = await this.prisma.user.findFirst({
246
258
  where: { id: userId },
247
259
  select: {
@@ -250,7 +262,7 @@ export class ProfileService {
250
262
  },
251
263
  });
252
264
 
253
- const issuer = this.settings['mfa-issuer'] ?? 'Hedhog';
265
+ const issuer = settings['mfa-issuer'] ?? 'Hedhog';
254
266
  const userEmail = user.user_identifier.find(ui => ui.type === 'email').value
255
267
  const appName = `${issuer} (${userEmail})`;
256
268
 
@@ -261,7 +273,7 @@ export class ProfileService {
261
273
  encoding: 'base32',
262
274
  });
263
275
 
264
- const step = this.settings['mfa-setp'] ?? 30;
276
+ const step = settings['mfa-setp'] ?? 30;
265
277
  const otpauth = speakeasy.otpauthURL({
266
278
  secret: secret.base32,
267
279
  label: `${issuer}:${userEmail}`,
@@ -946,7 +958,20 @@ export class ProfileService {
946
958
  throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found.'));
947
959
  }
948
960
 
949
- const rpName = this.settings['system-name'] || 'HedHog';
961
+ const settings = await this.setting.getSettingValues([
962
+ 'mfa-issuer',
963
+ 'mfa-window',
964
+ 'mfa-setp',
965
+ 'system-name',
966
+ 'google_client_id',
967
+ 'google_client_secret',
968
+ 'google_scopes',
969
+ 'url',
970
+ 'mfa-challenge-expiration-minutes',
971
+ 'access-token-expiration-minutes'
972
+ ]);
973
+
974
+ const rpName = settings['system-name'] || 'HedHog';
950
975
  const rpID = new URL(process.env.FRONTEND_URL || 'http://localhost:3200').hostname;
951
976
  const userEmail = user.user_identifier.find(ui => ui.type === 'email')?.value || `user${userId}@example.com`;
952
977
  const existingAuthenticators = user.user_mfa
@@ -2,7 +2,7 @@ import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
3
3
  import { PrismaService } from '@hed-hog/api-prisma';
4
4
  import { HttpService } from '@nestjs/axios';
5
- import { BadRequestException, forwardRef, HttpException, HttpStatus, Inject, Injectable, OnModuleInit } from '@nestjs/common';
5
+ import { BadRequestException, forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
6
6
  import { firstValueFrom } from 'rxjs';
7
7
  import { SecurityService } from '../security/security.service';
8
8
  import { SettingService } from '../setting/setting.service';
@@ -21,9 +21,7 @@ type GeoIpResult = {
21
21
  };
22
22
 
23
23
  @Injectable()
24
- export class SessionService implements OnModuleInit {
25
-
26
- public settings: Record<string, any> = {};
24
+ export class SessionService {
27
25
 
28
26
  constructor(
29
27
  private prisma: PrismaService,
@@ -41,12 +39,6 @@ export class SessionService implements OnModuleInit {
41
39
  private readonly paginationService: PaginationService,
42
40
  ) { }
43
41
 
44
- async onModuleInit() {
45
- this.settings = await this.setting.getSettingValues([
46
- 'refresh-token-expiration-minutes'
47
- ]);
48
- }
49
-
50
42
  async create(locale: string, userId: number, ipAddress: string, userAgent: string) {
51
43
  if (!userId) {
52
44
  throw new BadRequestException(getLocaleText('session.userIdRequired', locale, 'User ID is required to create a session.'));
@@ -60,6 +52,9 @@ export class SessionService implements OnModuleInit {
60
52
  throw new BadRequestException(getLocaleText('session.userAgentRequired', locale, 'User agent is required to create a session.'));
61
53
  }
62
54
 
55
+ const settings = await this.setting.getSettingValues([
56
+ 'refresh-token-expiration-minutes'
57
+ ]);
63
58
  const token = await this.token.createOpaqueToken();
64
59
  const hash = this.security.hashWithPepper(token)
65
60
  const session = await this.prisma.user_session.create({
@@ -67,7 +62,7 @@ export class SessionService implements OnModuleInit {
67
62
  user_id: userId,
68
63
  ip_address: ipAddress,
69
64
  user_agent: userAgent,
70
- expires_at: new Date(Date.now() + ((this.settings['refresh-token-expiration-minutes'] || 43200) * 60 * 1000)),
65
+ expires_at: new Date(Date.now() + ((settings['refresh-token-expiration-minutes'] || 43200) * 60 * 1000)),
71
66
  hash
72
67
  },
73
68
  });
@@ -7,6 +7,7 @@ import {
7
7
  Delete,
8
8
  forwardRef,
9
9
  Get,
10
+ Header,
10
11
  Inject,
11
12
  Param,
12
13
  ParseIntPipe,
@@ -14,10 +15,12 @@ import {
14
15
  Post,
15
16
  Put,
16
17
  Query,
18
+ Res,
17
19
  UploadedFile,
18
- UseInterceptors,
20
+ UseInterceptors
19
21
  } from '@nestjs/common';
20
22
  import { FileInterceptor } from '@nestjs/platform-express';
23
+ import { Response } from 'express';
21
24
  import { ConfirmImportDTO } from './dto/confirm-import.dto';
22
25
  import { CreateDTO } from './dto/create.dto';
23
26
  import { DeleteDTO } from './dto/delete.dto';
@@ -36,8 +39,11 @@ export class SettingsController {
36
39
  ) { }
37
40
 
38
41
  @Get('export')
39
- async exportSettings(@Query() {secrets = false}: ExportDto) {
40
- return this.settingService.exportSettings(secrets);
42
+ @Header('Content-Type', 'application/octet-stream')
43
+ @Header('Content-Disposition', 'attachment; filename="settings.hedhog"')
44
+ async exportSettings(@Query() {secrets = false}: ExportDto, @Res() res: Response) {
45
+ const compressedData = await this.settingService.exportSettings(secrets);
46
+ res.send(compressedData);
41
47
  }
42
48
 
43
49
  @Public()
@@ -3,11 +3,12 @@ import { getLocaleText, LocaleService } from '@hed-hog/api-locale';
3
3
  import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
4
4
  import { PrismaService, setting_type_enum } from '@hed-hog/api-prisma';
5
5
  import {
6
- BadRequestException,
7
- forwardRef,
8
- Inject,
9
- Injectable,
6
+ BadRequestException,
7
+ forwardRef,
8
+ Inject,
9
+ Injectable,
10
10
  } from '@nestjs/common';
11
+ import * as pako from 'pako';
11
12
  import { CreateDTO } from './dto/create.dto';
12
13
  import { DeleteDTO } from './dto/delete.dto';
13
14
  import { SettingDTO } from './dto/setting.dto';
@@ -27,7 +28,7 @@ export class SettingService {
27
28
  private readonly localeService: LocaleService,
28
29
  ) { }
29
30
 
30
- async exportSettings(includeSecrets = false) {
31
+ async exportSettings(includeSecrets = false): Promise<Buffer> {
31
32
  const settings = await this.prismaService.setting.findMany({
32
33
  where: {
33
34
  ...( !includeSecrets ? {
@@ -42,16 +43,20 @@ export class SettingService {
42
43
  }
43
44
  });
44
45
 
45
- return settings;
46
+ // Converte para JSON e compacta com gzip
47
+ const jsonString = JSON.stringify(settings);
48
+ const compressed = pako.gzip(jsonString);
49
+ return Buffer.from(compressed);
46
50
  }
47
51
 
48
52
  async importSettings(file: MulterFile) {
49
- const content = file.buffer?.toString('utf-8') || '';
50
53
  let json;
51
54
  try {
52
- json = JSON.parse(content);
55
+ // Descompacta o arquivo .hedhog
56
+ const decompressed = pako.ungzip(file.buffer, { to: 'string' });
57
+ json = JSON.parse(decompressed);
53
58
  } catch (err) {
54
- throw new BadRequestException(getLocaleText('invalidJsonFile', 'en', 'File does not contain valid JSON.'));
59
+ throw new BadRequestException(getLocaleText('invalidCompressedFile', 'en', 'File is not a valid .hedhog file or is corrupted.'));
55
60
  }
56
61
 
57
62
  if (!Array.isArray(json)) {
@@ -74,11 +79,15 @@ export class SettingService {
74
79
  const validSlugs = slugsFromFile.filter((slug: string) => existingSlugs.includes(slug));
75
80
  const invalidSlugs = slugsFromFile.filter((slug: string) => !existingSlugs.includes(slug));
76
81
 
82
+ // Filtra apenas os dados válidos para retornar
83
+ const validData = json.filter((item: any) => validSlugs.includes(item.slug));
84
+
77
85
  return {
78
86
  totalSettings: slugsFromFile.length,
79
87
  validSettings: validSlugs.length,
80
88
  invalidSlugs,
81
89
  validSlugs,
90
+ fileData: validData,
82
91
  };
83
92
  }
84
93
 
@@ -1,13 +1,11 @@
1
1
  import { getLocaleText } from "@hed-hog/api-locale";
2
- import { ForbiddenException, forwardRef, Inject, Injectable, OnModuleInit } from "@nestjs/common";
2
+ import { ForbiddenException, forwardRef, Inject, Injectable } from "@nestjs/common";
3
3
  import { JwtService } from "@nestjs/jwt";
4
4
  import { SecurityService } from "../security/security.service";
5
5
  import { SettingService } from "../setting/setting.service";
6
6
 
7
7
  @Injectable()
8
- export class TokenService implements OnModuleInit {
9
-
10
- public settings: Record<string, any> = {};
8
+ export class TokenService {
11
9
 
12
10
  constructor(
13
11
  @Inject(forwardRef(() => JwtService))
@@ -18,12 +16,6 @@ export class TokenService implements OnModuleInit {
18
16
  private readonly setting: SettingService,
19
17
  ) { }
20
18
 
21
- async onModuleInit() {
22
- this.settings = await this.setting.getSettingValues([
23
- 'url'
24
- ]);
25
- }
26
-
27
19
  async verify(locale: string, token: string) {
28
20
  try {
29
21
  return this.jwt.verifyAsync(token, { secret: this.security.getJwtSecret() });
@@ -42,10 +34,15 @@ export class TokenService implements OnModuleInit {
42
34
  return this.security.randomOpaque(size);
43
35
  }
44
36
 
45
- private getCookieDomain(): string | undefined {
46
- if (this.settings['url']) {
37
+ private async getCookieDomain() {
38
+
39
+ const settings = await this.setting.getSettingValues([
40
+ 'url'
41
+ ]);
42
+
43
+ if (settings['url']) {
47
44
  try {
48
- return new URL(this.settings['url']).hostname;
45
+ return new URL(settings['url']).hostname;
49
46
  } catch {
50
47
  return process.env.COOKIE_DOMAIN;
51
48
  }
@@ -55,7 +52,7 @@ export class TokenService implements OnModuleInit {
55
52
 
56
53
  async removeRefreshTokenCookie(res): Promise<void> {
57
54
 
58
- const domain = this.getCookieDomain();
55
+ const domain = await this.getCookieDomain();
59
56
  const isLocalhost = !domain || domain === 'localhost';
60
57
  const cookieOptions: any = {
61
58
  httpOnly: true,
@@ -69,7 +66,7 @@ export class TokenService implements OnModuleInit {
69
66
  }
70
67
 
71
68
  async setRefreshTokenCookie(locale: string, res, token: string, expires_at: Date): Promise<void> {
72
- const domain = this.getCookieDomain();
69
+ const domain = await this.getCookieDomain();
73
70
  const isLocalhost = !domain || domain === 'localhost';
74
71
  const maxAge = expires_at.getTime() - Date.now();
75
72
  const cookieOptions: any = {