@hed-hog/core 0.0.262 → 0.0.270

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 (40) hide show
  1. package/dist/auth/auth.controller.d.ts +1 -1
  2. package/dist/auth/auth.service.d.ts +1 -1
  3. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +15 -15
  4. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +15 -15
  5. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +7 -7
  6. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +7 -7
  7. package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
  8. package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
  9. package/dist/file/file.controller.d.ts +2 -2
  10. package/dist/file/file.service.d.ts +4 -4
  11. package/dist/install/install.module.d.ts.map +1 -1
  12. package/dist/install/install.module.js +2 -0
  13. package/dist/install/install.module.js.map +1 -1
  14. package/dist/install/install.service.d.ts +3 -2
  15. package/dist/install/install.service.d.ts.map +1 -1
  16. package/dist/install/install.service.js +33 -56
  17. package/dist/install/install.service.js.map +1 -1
  18. package/dist/menu/menu.controller.d.ts +3 -3
  19. package/dist/menu/menu.service.d.ts +3 -3
  20. package/dist/setting/setting.service.d.ts.map +1 -1
  21. package/dist/setting/setting.service.js +2 -1
  22. package/dist/setting/setting.service.js.map +1 -1
  23. package/dist/user/user.controller.d.ts +2 -2
  24. package/dist/user/user.service.d.ts +3 -3
  25. package/hedhog/data/dashboard_component.yaml +8 -8
  26. package/hedhog/data/dashboard_item.yaml +18 -18
  27. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +1 -1
  28. package/hedhog/frontend/app/configurations/layout.tsx.ejs +2 -2
  29. package/hedhog/frontend/app/configurations/page.tsx.ejs +2 -2
  30. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +10 -8
  31. package/hedhog/frontend/app/dashboard/components/draggable-grid.tsx.ejs +54 -26
  32. package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +6 -6
  33. package/hedhog/frontend/app/dashboard/dashboard.css.ejs +92 -184
  34. package/hedhog/frontend/app/preferences/page.tsx.ejs +1 -1
  35. package/hedhog/frontend/app/roles/page.tsx.ejs +59 -57
  36. package/hedhog/frontend/app/users/page.tsx.ejs +1 -1
  37. package/package.json +14 -7
  38. package/src/install/install.module.ts +2 -0
  39. package/src/install/install.service.ts +42 -65
  40. package/src/setting/setting.service.ts +5 -4
@@ -1,157 +1,62 @@
1
- .react-grid-layout {
2
- position: relative;
3
- transition: height 200ms ease;
4
- }
5
-
6
- .react-grid-item {
7
- transition: all 200ms ease;
8
- transition-property: left, top, width, height;
9
- }
10
-
11
- .react-grid-item img {
12
- pointer-events: none;
13
- user-select: none;
14
- }
15
-
16
- .react-grid-item > .react-resizable-handle {
17
- position: absolute;
18
- width: 30px;
19
- height: 30px;
20
- z-index: 10;
21
- }
22
-
23
- .react-grid-item > .react-resizable-handle::after {
24
- content: '';
25
- position: absolute;
26
- width: 12px;
27
- height: 12px;
28
- background-color: hsl(var(--primary));
29
- border: 2px solid white;
30
- border-radius: 2px;
31
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
32
- opacity: 0.3;
33
- transition:
34
- opacity 0.2s ease,
35
- transform 0.2s ease;
36
- }
37
-
38
- .react-grid-item:hover > .react-resizable-handle::after {
39
- opacity: 1;
40
- transform: scale(1.2);
41
- }
42
-
43
- .react-resizable-hide > .react-resizable-handle {
44
- display: none;
45
- }
46
-
47
- .react-grid-item > .react-resizable-handle.react-resizable-handle-sw {
48
- bottom: 0;
49
- left: 0;
50
- cursor: sw-resize;
51
- }
52
-
53
- .react-grid-item > .react-resizable-handle.react-resizable-handle-sw::after {
54
- left: 2px;
55
- bottom: 2px;
56
- }
57
-
58
- .react-grid-item > .react-resizable-handle.react-resizable-handle-se {
59
- bottom: 0;
60
- right: 0;
61
- cursor: se-resize;
62
- }
63
-
64
- .react-grid-item > .react-resizable-handle.react-resizable-handle-se::after {
65
- right: 2px;
66
- bottom: 2px;
67
- }
68
-
69
- .react-grid-item > .react-resizable-handle.react-resizable-handle-nw {
70
- top: 0;
71
- left: 0;
72
- cursor: nw-resize;
73
- }
74
-
75
- .react-grid-item > .react-resizable-handle.react-resizable-handle-nw::after {
76
- left: 2px;
77
- top: 2px;
78
- }
79
-
80
- .react-grid-item > .react-resizable-handle.react-resizable-handle-ne {
81
- top: 0;
82
- right: 0;
83
- cursor: ne-resize;
84
- }
85
-
86
- .react-grid-item > .react-resizable-handle.react-resizable-handle-ne::after {
87
- right: 2px;
88
- top: 2px;
89
- }
90
-
91
- .react-grid-item > .react-resizable-handle.react-resizable-handle-w {
92
- left: 0;
93
- top: 50%;
94
- transform: translateY(-50%);
95
- cursor: ew-resize;
96
- width: 20px;
97
- height: 60px;
98
- }
99
-
100
- .react-grid-item > .react-resizable-handle.react-resizable-handle-w::after {
101
- left: 2px;
102
- top: 50%;
103
- transform: translateY(-50%);
104
- }
105
-
106
- .react-grid-item > .react-resizable-handle.react-resizable-handle-e {
107
- right: 0;
108
- top: 50%;
109
- transform: translateY(-50%);
110
- cursor: ew-resize;
111
- width: 20px;
112
- height: 60px;
113
- }
114
-
115
- .react-grid-item > .react-resizable-handle.react-resizable-handle-e::after {
116
- right: 2px;
117
- top: 50%;
118
- transform: translateY(-50%);
119
- }
120
-
121
- .react-grid-item > .react-resizable-handle.react-resizable-handle-n {
122
- top: 0;
123
- left: 50%;
124
- transform: translateX(-50%);
125
- cursor: ns-resize;
126
- width: 60px;
127
- height: 20px;
128
- }
129
-
130
- .react-grid-item > .react-resizable-handle.react-resizable-handle-n::after {
131
- top: 2px;
132
- left: 50%;
133
- transform: translateX(-50%);
134
- }
135
-
136
- .react-grid-item > .react-resizable-handle.react-resizable-handle-s {
137
- bottom: 0;
138
- left: 50%;
139
- transform: translateX(-50%);
140
- cursor: ns-resize;
141
- width: 60px;
142
- height: 20px;
143
- }
144
-
145
- .react-grid-item > .react-resizable-handle.react-resizable-handle-s::after {
146
- bottom: 2px;
147
- left: 50%;
148
- transform: translateX(-50%);
149
- }
150
-
151
- /* Drag handle styles */
152
- .drag-handle {
153
- cursor: grab !important;
154
- user-select: none !important;
1
+ .dashboard-grid.react-grid-layout {
2
+ position: relative;
3
+ transition: height 200ms ease;
4
+ }
5
+
6
+ .dashboard-grid .react-grid-item {
7
+ transition: all 200ms ease;
8
+ transition-property: left, top, width, height;
9
+ }
10
+
11
+ .dashboard-grid .react-grid-item img {
12
+ pointer-events: none;
13
+ user-select: none;
14
+ }
15
+
16
+ .dashboard-grid .react-grid-item > .react-resizable-handle {
17
+ position: absolute;
18
+ width: 18px;
19
+ height: 18px;
20
+ z-index: 10;
21
+ opacity: 0;
22
+ transition: opacity 0.15s ease;
23
+ }
24
+
25
+ .dashboard-grid .react-grid-item > .react-resizable-handle::after {
26
+ content: '';
27
+ position: absolute;
28
+ right: 3px;
29
+ bottom: 3px;
30
+ width: 6px;
31
+ height: 6px;
32
+ border-right: 2px solid hsl(var(--muted-foreground));
33
+ border-bottom: 2px solid hsl(var(--muted-foreground));
34
+ opacity: 0.45;
35
+ }
36
+
37
+ .dashboard-grid .react-grid-item:hover > .react-resizable-handle.react-resizable-handle-se,
38
+ .dashboard-grid .react-grid-item.resizing > .react-resizable-handle.react-resizable-handle-se {
39
+ opacity: 1;
40
+ }
41
+
42
+ .dashboard-grid .react-resizable-hide > .react-resizable-handle {
43
+ display: none;
44
+ }
45
+
46
+ .dashboard-grid .react-grid-item > .react-resizable-handle:not(.react-resizable-handle-se) {
47
+ display: none;
48
+ }
49
+
50
+ .dashboard-grid .react-grid-item > .react-resizable-handle.react-resizable-handle-se {
51
+ bottom: 0;
52
+ right: 0;
53
+ cursor: se-resize;
54
+ }
55
+
56
+ /* Drag handle styles */
57
+ .drag-handle {
58
+ cursor: grab !important;
59
+ user-select: none !important;
155
60
  }
156
61
 
157
62
  .drag-handle:active {
@@ -159,38 +64,41 @@
159
64
  }
160
65
 
161
66
  /* Prevent text selection during drag */
162
- .react-grid-item.react-draggable-dragging {
163
- transition: none;
164
- z-index: 100;
165
- will-change: transform;
166
- opacity: 0.9;
167
- }
168
-
169
- .react-grid-item.resizing {
170
- transition: none;
171
- z-index: 100;
172
- will-change: transform;
173
- opacity: 0.9;
174
- outline: 2px solid hsl(var(--primary));
175
- outline-offset: 2px;
176
- }
177
-
178
- /* Placeholder styling */
179
- .react-grid-placeholder {
180
- background: hsl(var(--primary) / 0.2);
181
- opacity: 0.5;
182
- transition-duration: 100ms;
67
+ .dashboard-grid .react-grid-item.react-draggable-dragging {
68
+ transition: none;
69
+ z-index: 100;
70
+ will-change: transform;
71
+ opacity: 0.9;
72
+ }
73
+
74
+ .dashboard-grid .react-grid-item.resizing {
75
+ transition: none;
76
+ z-index: 100;
77
+ will-change: transform;
78
+ opacity: 0.9;
79
+ }
80
+
81
+ /* Placeholder styling */
82
+ .dashboard-grid .react-grid-placeholder {
83
+ background: hsl(var(--primary) / 0.2);
84
+ opacity: 0.5;
85
+ transition-duration: 100ms;
183
86
  z-index: 2;
184
87
  border-radius: 0.5rem;
185
88
  border: 2px dashed hsl(var(--primary));
186
89
  }
187
90
 
188
- .react-grid-item:hover {
189
- outline: 1px solid hsl(var(--primary) / 0.2);
190
- outline-offset: -1px;
191
- }
192
-
193
- .react-grid-item:hover > .react-resizable-handle::after {
194
- opacity: 1;
195
- transform: scale(1.1);
196
- }
91
+ .dashboard-grid .react-grid-item:hover {
92
+ outline: 1px solid hsl(var(--primary) / 0.12);
93
+ outline-offset: -1px;
94
+ }
95
+
96
+ .dashboard-widget > [data-slot='card'] {
97
+ gap: 0.75rem;
98
+ padding-top: 0.75rem;
99
+ padding-bottom: 0.75rem;
100
+ }
101
+
102
+ .dashboard-widget > [data-slot='card'] > [data-slot='card-header'] {
103
+ padding-top: 0;
104
+ }
@@ -11,7 +11,7 @@ import {
11
11
  SelectTrigger,
12
12
  SelectValue,
13
13
  } from '@/components/ui/select';
14
- import { PaginatedResult } from '@hed-hog/api-pagination';
14
+ import { PaginatedResult } from '@/types/pagination-result';
15
15
  import { Setting, SettingList } from '@hed-hog/api-types';
16
16
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
17
17
  import { zodResolver } from '@hookform/resolvers/zod';
@@ -223,7 +223,7 @@ export default function RolePage() {
223
223
  }
224
224
  };
225
225
 
226
- const handleEdit = async (role: Role) => {
226
+ const handleEdit = async (role: Role & { role_id: number }) => {
227
227
  setEditFormError(null);
228
228
 
229
229
  try {
@@ -426,65 +426,67 @@ export default function RolePage() {
426
426
  <p className="text-sm text-muted-foreground">{t('noRolesFound')}</p>
427
427
  ) : (
428
428
  <div className="grid gap-4 grid-cols-1 lg:grid-cols-2 xl:grid-cols-3">
429
- {rolesResponse?.data?.map((role: Role & { role_id: number }) => (
430
- <Card
431
- key={String(role.role_id)}
432
- onDoubleClick={() => handleEdit(role)}
433
- className="cursor-pointer rounded-md flex flex-col justify-between gap-2 border border-border/60 bg-card p-4 shadow-sm transition hover:border-primary"
434
- >
435
- <CardHeader className="flex items-start justify-between gap-4 p-0">
436
- <div className="flex items-center gap-3 flex-1">
437
- <div className="h-12 w-12 shrink-0 rounded-full bg-primary/10 flex items-center justify-center">
438
- <ShieldCheck className="h-6 w-6 text-primary" />
439
- </div>
440
- <div className="flex-1">
441
- <CardTitle className="text-sm font-semibold">
442
- {role.name}
443
- </CardTitle>
444
- <CardDescription className="text-xs text-muted-foreground">
445
- {role.slug}
446
- </CardDescription>
447
- </div>
448
- </div>
449
- <Button
450
- variant="outline"
451
- size="sm"
452
- onClick={() => handleEdit(role)}
453
- >
454
- {t('buttonEditRole')}
455
- </Button>
456
- </CardHeader>
457
- {role.description && (
458
- <CardContent className="p-0">
459
- <p className="text-xs text-muted-foreground line-clamp-2">
460
- {role.description}
461
- </p>
462
- <div className="text-xs line-clamp-2 flex gap-2 py-2">
463
- <div>
464
- {(role as any).user_count}{' '}
465
- {(role as any).user_count === 1
466
- ? t('user')
467
- : t('users')}
468
- </div>
469
- <div>•</div>
470
- <div>
471
- {(role as any).menu_count}{' '}
472
- {(role as any).menu_count === 1
473
- ? t('menu')
474
- : t('menus')}
429
+ {(rolesResponse?.data as (Role & { role_id: number })[])?.map(
430
+ (role: Role & { role_id: number }) => (
431
+ <Card
432
+ key={String(role.role_id)}
433
+ onDoubleClick={() => handleEdit(role)}
434
+ className="cursor-pointer rounded-md flex flex-col justify-between gap-2 border border-border/60 bg-card p-4 shadow-sm transition hover:border-primary"
435
+ >
436
+ <CardHeader className="flex items-start justify-between gap-4 p-0">
437
+ <div className="flex items-center gap-3 flex-1">
438
+ <div className="h-12 w-12 shrink-0 rounded-full bg-primary/10 flex items-center justify-center">
439
+ <ShieldCheck className="h-6 w-6 text-primary" />
475
440
  </div>
476
- <div>•</div>
477
- <div>
478
- {(role as any).route_count}{' '}
479
- {(role as any).route_count === 1
480
- ? t('route')
481
- : t('routes')}
441
+ <div className="flex-1">
442
+ <CardTitle className="text-sm font-semibold">
443
+ {role.name}
444
+ </CardTitle>
445
+ <CardDescription className="text-xs text-muted-foreground">
446
+ {role.slug}
447
+ </CardDescription>
482
448
  </div>
483
449
  </div>
484
- </CardContent>
485
- )}
486
- </Card>
487
- ))}
450
+ <Button
451
+ variant="outline"
452
+ size="sm"
453
+ onClick={() => handleEdit(role)}
454
+ >
455
+ {t('buttonEditRole')}
456
+ </Button>
457
+ </CardHeader>
458
+ {role.description && (
459
+ <CardContent className="p-0">
460
+ <p className="text-xs text-muted-foreground line-clamp-2">
461
+ {role.description}
462
+ </p>
463
+ <div className="text-xs line-clamp-2 flex gap-2 py-2">
464
+ <div>
465
+ {(role as any).user_count}{' '}
466
+ {(role as any).user_count === 1
467
+ ? t('user')
468
+ : t('users')}
469
+ </div>
470
+ <div>•</div>
471
+ <div>
472
+ {(role as any).menu_count}{' '}
473
+ {(role as any).menu_count === 1
474
+ ? t('menu')
475
+ : t('menus')}
476
+ </div>
477
+ <div>•</div>
478
+ <div>
479
+ {(role as any).route_count}{' '}
480
+ {(role as any).route_count === 1
481
+ ? t('route')
482
+ : t('routes')}
483
+ </div>
484
+ </div>
485
+ </CardContent>
486
+ )}
487
+ </Card>
488
+ )
489
+ )}
488
490
  </div>
489
491
  )}
490
492
 
@@ -41,7 +41,7 @@ import {
41
41
  import { formatDateTime } from '@/lib/format-date';
42
42
  import { getPhotoUrl } from '@/lib/get-photo-url';
43
43
  import { getUserEmail } from '@/lib/get-user-email';
44
- import { PaginatedResult } from '@hed-hog/api-pagination';
44
+ import { PaginatedResult } from '@/types/pagination-result';
45
45
  import { User, UserMfa } from '@hed-hog/api-types';
46
46
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
47
47
  import { zodResolver } from '@hookform/resolvers/zod';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.262",
3
+ "version": "0.0.270",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -30,12 +30,12 @@
30
30
  "sharp": "^0.34.2",
31
31
  "speakeasy": "^2.0.0",
32
32
  "uuid": "^11.1.0",
33
- "@hed-hog/api-pagination": "0.0.5",
34
- "@hed-hog/api-locale": "0.0.11",
35
- "@hed-hog/api-mail": "0.0.7",
36
- "@hed-hog/api-prisma": "0.0.4",
37
- "@hed-hog/api-types": "0.0.1",
38
- "@hed-hog/api": "0.0.3"
33
+ "@hed-hog/api-pagination": "0.0.6",
34
+ "@hed-hog/api-prisma": "0.0.5",
35
+ "@hed-hog/api-locale": "0.0.13",
36
+ "@hed-hog/api-mail": "0.0.8",
37
+ "@hed-hog/api": "0.0.4",
38
+ "@hed-hog/api-types": "0.0.1"
39
39
  },
40
40
  "exports": {
41
41
  ".": {
@@ -44,11 +44,18 @@
44
44
  "require": "./dist/index.js",
45
45
  "default": "./dist/index.js"
46
46
  },
47
+ "./*": {
48
+ "types": "./dist/*.d.ts",
49
+ "import": "./dist/*.js",
50
+ "require": "./dist/*.js",
51
+ "default": "./dist/*.js"
52
+ },
47
53
  "./package.json": "./package.json"
48
54
  },
49
55
  "typesVersions": {
50
56
  "*": {
51
57
  "*": [
58
+ "./dist/*.d.ts",
52
59
  "./dist/index.d.ts"
53
60
  ]
54
61
  }
@@ -1,5 +1,6 @@
1
1
  import { PrismaModule } from '@hed-hog/api-prisma';
2
2
  import { forwardRef, Module } from '@nestjs/common';
3
+ import { ConfigModule } from '@nestjs/config';
3
4
  import { AuthModule } from '../auth/auth.module';
4
5
  import { SecurityModule } from '../security/security.module';
5
6
  import { SettingModule } from '../setting/setting.module';
@@ -12,6 +13,7 @@ import { InstallService } from './install.service';
12
13
  forwardRef(() => SettingModule),
13
14
  forwardRef(() => SecurityModule),
14
15
  forwardRef(() => AuthModule),
16
+ forwardRef(() => ConfigModule),
15
17
  ],
16
18
  controllers: [InstallController],
17
19
  providers: [InstallService],
@@ -1,5 +1,6 @@
1
1
  import { PrismaService } from '@hed-hog/api-prisma';
2
2
  import { BadRequestException, Injectable, Logger } from '@nestjs/common';
3
+ import { ConfigService } from '@nestjs/config';
3
4
  import { existsSync, readFileSync } from 'fs';
4
5
  import { writeFile } from 'fs/promises';
5
6
  import { resolve } from 'path';
@@ -13,80 +14,50 @@ export class InstallService {
13
14
 
14
15
  constructor(
15
16
  private readonly security: SecurityService,
16
- private readonly prisma: PrismaService
17
+ private readonly prisma: PrismaService,
18
+ private readonly configService: ConfigService,
17
19
  ) { }
18
20
 
19
21
  private async forceReset() {
20
22
 
21
23
  await new Promise((resolve) => setTimeout(resolve, 1000));
22
24
 
23
- //leia o arquivo src/main.ts e salveo com o mesmo conteudo para forcar o nestjs a reiniciar a aplicacao
24
- const mainFilePath = resolve(process.cwd(), 'src', 'main.ts');
25
- if (!existsSync(mainFilePath)) {
26
- throw new BadRequestException('main.ts file not found.');
25
+ // In development, touching main.ts triggers restart; in production only dist exists.
26
+ const candidatePaths = [
27
+ resolve(process.cwd(), 'src', 'main.ts'),
28
+ resolve(process.cwd(), 'dist', 'apps', 'api', 'src', 'main.js'),
29
+ resolve(process.cwd(), 'dist', 'src', 'main.js'),
30
+ ];
31
+ const mainFilePath = candidatePaths.find((filePath) => existsSync(filePath));
32
+ if (!mainFilePath) {
33
+ this.logger.warn('Skip force reset: no main entry file found to touch.');
34
+ return;
27
35
  }
36
+
28
37
  let mainContent: string;
29
38
  try {
30
39
  mainContent = readFileSync(mainFilePath, 'utf-8');
31
40
  } catch (err: any) {
32
- throw new BadRequestException(`Failed to read main.ts file: ${err.message}`);
41
+ throw new BadRequestException(
42
+ `Failed to read main entry file: ${err.message}`,
43
+ );
33
44
  }
34
45
  try {
35
- this.logger.verbose('Forcing application restart by touching main.ts...');
46
+ this.logger.verbose(
47
+ `Forcing application restart by touching ${mainFilePath}...`,
48
+ );
36
49
  await writeFile(mainFilePath, mainContent, 'utf-8');
37
50
  } catch (err: any) {
38
- throw new BadRequestException(`Failed to write main.ts file: ${err.message}`);
51
+ throw new BadRequestException(
52
+ `Failed to write main entry file: ${err.message}`,
53
+ );
39
54
  }
40
55
 
41
56
  }
42
57
 
43
58
  private async checkInstallation() {
44
-
45
- const hedhogFilePath = resolve(process.cwd(), '..', '..', 'hedhog.json');
46
-
47
- let data = {
48
- installed: false
49
- };
50
-
51
- if (existsSync(hedhogFilePath)) {
52
- try {
53
- const fileContent = readFileSync(hedhogFilePath, 'utf-8');
54
- data = JSON.parse(fileContent);
55
- } catch (err: any) {
56
- this.logger.warn(`Failed to read hedhog.json: ${err.message}`);
57
- }
58
- }
59
-
60
- return data.installed === true;
61
-
62
- }
63
-
64
- private async markAsInstalled() {
65
-
66
- const hedhogFilePath = resolve(process.cwd(), '..', '..', 'hedhog.json');
67
-
68
- let content: any = {}
69
-
70
- if (existsSync(hedhogFilePath)) {
71
- try {
72
- const fileContent = readFileSync(hedhogFilePath, 'utf-8');
73
- content = JSON.parse(fileContent);
74
- } catch (err: any) {
75
- this.logger.warn(`Failed to read hedhog.json: ${err.message}`);
76
- }
77
- }
78
-
79
- content.installed = true;
80
- content.installedAt = new Date().toISOString();
81
-
82
- try {
83
- this.logger.verbose('Marking application as installed in hedghog.json...');
84
- await writeFile(hedhogFilePath, JSON.stringify(content, null, 2), 'utf-8');
85
- } catch (err: any) {
86
- throw new BadRequestException(`Failed to write hedhog.json: ${err.message}`);
87
- }
88
-
89
- return true;
59
+ const usersCount = await this.prisma.user.count();
60
+ return usersCount > 0;
90
61
  }
91
62
 
92
63
  private async updateEnvSecrets(pepper: string) {
@@ -170,8 +141,6 @@ export class InstallService {
170
141
  throw new BadRequestException('Application is already installed.');
171
142
  }
172
143
 
173
- const pepper = this.base64Encode(this.security.randomOpaque(16));
174
-
175
144
  await this.prisma.$transaction(async (prisma) => {
176
145
 
177
146
  this.logger.log('Starting installation process...');
@@ -220,6 +189,13 @@ export class InstallService {
220
189
 
221
190
  this.logger.log(`Creating admin user: ${userName} <${email}>`);
222
191
 
192
+ const pepper = this.configService.get<string>('PEPPER');
193
+ if (!pepper) {
194
+ throw new BadRequestException(
195
+ 'PEPPER is not configured. Set PEPPER before installation.',
196
+ );
197
+ }
198
+
223
199
  const passwordHash = await this.security.hashArgon2(password, pepper);
224
200
 
225
201
  const user = await prisma.user.create({
@@ -260,9 +236,15 @@ export class InstallService {
260
236
 
261
237
  this.logger.log(`Roles assigned to user ID: ${user.id}`);
262
238
 
263
- await this.markAsInstalled();
264
- await this.updateEnvSecrets(pepper);
265
- this.forceReset();
239
+ const isDevelopment = process.env.NODE_ENV !== 'production';
240
+ if (isDevelopment) {
241
+ await this.updateEnvSecrets(pepper);
242
+ await this.forceReset();
243
+ } else {
244
+ this.logger.log(
245
+ 'Skipping env secret update and force reset outside development.',
246
+ );
247
+ }
266
248
 
267
249
  this.logger.log('Installation process completed successfully.');
268
250
 
@@ -273,12 +255,7 @@ export class InstallService {
273
255
  }
274
256
 
275
257
  async check() {
276
- if (await this.checkInstallation()) {
277
- return { success: true };
278
- } else {
279
- throw new BadRequestException('Application is not installed.');
280
- }
281
-
258
+ return { success: true };
282
259
  }
283
260
 
284
261
  async generateMailMigration({