@hed-hog/core 0.0.266 → 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.
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +15 -15
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +15 -15
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +7 -7
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +7 -7
- package/dist/file/file.controller.d.ts +2 -2
- package/dist/file/file.service.d.ts +4 -4
- package/dist/install/install.module.d.ts.map +1 -1
- package/dist/install/install.module.js +2 -0
- package/dist/install/install.module.js.map +1 -1
- package/dist/install/install.service.d.ts +3 -2
- package/dist/install/install.service.d.ts.map +1 -1
- package/dist/install/install.service.js +33 -56
- package/dist/install/install.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +2 -1
- package/dist/setting/setting.service.js.map +1 -1
- package/hedhog/data/dashboard_component.yaml +8 -8
- package/hedhog/data/dashboard_item.yaml +18 -18
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +10 -8
- package/hedhog/frontend/app/dashboard/components/draggable-grid.tsx.ejs +20 -18
- package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +6 -6
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +92 -184
- package/package.json +12 -5
- package/src/install/install.module.ts +2 -0
- package/src/install/install.service.ts +42 -65
- package/src/setting/setting.service.ts +5 -4
|
@@ -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
|
-
//
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
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(
|
|
41
|
+
throw new BadRequestException(
|
|
42
|
+
`Failed to read main entry file: ${err.message}`,
|
|
43
|
+
);
|
|
33
44
|
}
|
|
34
45
|
try {
|
|
35
|
-
this.logger.verbose(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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({
|
|
@@ -181,10 +181,11 @@ export class SettingService {
|
|
|
181
181
|
'date-format',
|
|
182
182
|
'time-format',
|
|
183
183
|
'timezone',
|
|
184
|
-
'menu-width',
|
|
185
|
-
'providers',
|
|
186
|
-
'disable-authentication-with-email-and-password'
|
|
187
|
-
|
|
184
|
+
'menu-width',
|
|
185
|
+
'providers',
|
|
186
|
+
'disable-authentication-with-email-and-password',
|
|
187
|
+
'contact-allow-company-registration',
|
|
188
|
+
];
|
|
188
189
|
const setting = await this.getSettingValues(slugs);
|
|
189
190
|
return { locales, setting };
|
|
190
191
|
}
|