@hed-hog/core 0.0.300 → 0.0.302

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/ai/ai.service.d.ts +13 -2
  2. package/dist/ai/ai.service.d.ts.map +1 -1
  3. package/dist/ai/ai.service.js +104 -2
  4. package/dist/ai/ai.service.js.map +1 -1
  5. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +26 -9
  6. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard-core/dashboard-core.controller.js +11 -5
  8. package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
  9. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +34 -10
  10. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard-core/dashboard-core.service.js +196 -69
  12. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/integration/services/integration-link.service.d.ts +5 -1
  18. package/dist/integration/services/integration-link.service.d.ts.map +1 -1
  19. package/dist/integration/services/integration-link.service.js +141 -53
  20. package/dist/integration/services/integration-link.service.js.map +1 -1
  21. package/dist/mail/mail.service.d.ts +9 -2
  22. package/dist/mail/mail.service.d.ts.map +1 -1
  23. package/dist/mail/mail.service.js +56 -4
  24. package/dist/mail/mail.service.js.map +1 -1
  25. package/dist/setting/setting.service.d.ts +6 -1
  26. package/dist/setting/setting.service.d.ts.map +1 -1
  27. package/dist/setting/setting.service.js +188 -15
  28. package/dist/setting/setting.service.js.map +1 -1
  29. package/hedhog/data/setting_group.yaml +28 -0
  30. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +305 -75
  31. package/hedhog/frontend/messages/en.json +15 -3
  32. package/hedhog/frontend/messages/pt.json +15 -3
  33. package/package.json +5 -5
  34. package/src/ai/ai.service.ts +129 -1
  35. package/src/dashboard/dashboard-core/dashboard-core.controller.ts +9 -2
  36. package/src/dashboard/dashboard-core/dashboard-core.service.ts +276 -75
  37. package/src/index.ts +7 -6
  38. package/src/integration/services/integration-link.service.ts +190 -55
  39. package/src/mail/mail.service.ts +67 -3
  40. package/src/setting/setting.service.ts +222 -15
@@ -487,6 +487,61 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
487
487
  });
488
488
  return roleUsers.map((roleUser) => roleUser.role_id);
489
489
  }
490
+ async getDashboardComponentRoleRequirements(dashboardId) {
491
+ const dashboardItems = await this.prismaService.dashboard_item.findMany({
492
+ where: {
493
+ dashboard_id: dashboardId,
494
+ },
495
+ select: {
496
+ component_id: true,
497
+ dashboard_component: {
498
+ select: {
499
+ dashboard_component_role: {
500
+ select: {
501
+ role_id: true,
502
+ },
503
+ },
504
+ },
505
+ },
506
+ },
507
+ });
508
+ const uniqueByComponentId = new Map();
509
+ for (const item of dashboardItems) {
510
+ if (uniqueByComponentId.has(item.component_id)) {
511
+ continue;
512
+ }
513
+ uniqueByComponentId.set(item.component_id, item.dashboard_component.dashboard_component_role.map((relation) => relation.role_id));
514
+ }
515
+ return Array.from(uniqueByComponentId.values());
516
+ }
517
+ userHasRequiredRolesForDashboard(componentRoleRequirements, userRoleIds) {
518
+ if (componentRoleRequirements.length === 0) {
519
+ return true;
520
+ }
521
+ if (userRoleIds.length === 0) {
522
+ return componentRoleRequirements.every((requiredRoles) => requiredRoles.length === 0);
523
+ }
524
+ const userRoleIdSet = new Set(userRoleIds);
525
+ return componentRoleRequirements.every((requiredRoles) => requiredRoles.length === 0 ||
526
+ requiredRoles.some((roleId) => userRoleIdSet.has(roleId)));
527
+ }
528
+ async getDashboardRoleAccessState(dashboardId, userId) {
529
+ const [componentRoleRequirements, userRoleIds] = await Promise.all([
530
+ this.getDashboardComponentRoleRequirements(dashboardId),
531
+ this.getUserRoleIds(userId),
532
+ ]);
533
+ const hasRequiredRoles = this.userHasRequiredRolesForDashboard(componentRoleRequirements, userRoleIds);
534
+ return {
535
+ hasRequiredRoles,
536
+ accessStatus: hasRequiredRoles ? 'allowed' : 'missing-roles',
537
+ };
538
+ }
539
+ async assertDashboardRoleAccess(dashboardId, userId) {
540
+ const { hasRequiredRoles } = await this.getDashboardRoleAccessState(dashboardId, userId);
541
+ if (!hasRequiredRoles) {
542
+ throw new common_1.ForbiddenException('Access denied to this dashboard');
543
+ }
544
+ }
490
545
  async getAccessibleTemplateOrThrow(userId, templateSlug, locale) {
491
546
  const userRoleIds = await this.getUserRoleIds(userId);
492
547
  const templateAccessFilter = userRoleIds.length > 0
@@ -821,6 +876,7 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
821
876
  if (!canAccess) {
822
877
  return [];
823
878
  }
879
+ await this.assertDashboardRoleAccess(dashboard.id, userId);
824
880
  const dashboardItems = await this.prismaService.dashboard_item.findMany({
825
881
  where: {
826
882
  dashboard_id: dashboard.id,
@@ -880,6 +936,7 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
880
936
  if (!canAccess) {
881
937
  throw new common_1.ForbiddenException('Access denied to this dashboard');
882
938
  }
939
+ await this.assertDashboardRoleAccess(dashboard.id, userId);
883
940
  const layoutUpdates = layout.flatMap((item) => {
884
941
  const itemId = Number.parseInt(item.i.replace('widget-', ''), 10);
885
942
  if (Number.isNaN(itemId)) {
@@ -924,6 +981,7 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
924
981
  if (!canAccess) {
925
982
  throw new common_1.ForbiddenException('Access denied to this dashboard');
926
983
  }
984
+ await this.assertDashboardRoleAccess(dashboard.id, userId);
927
985
  const userRoles = await this.prismaService.role_user.findMany({
928
986
  where: { user_id: userId },
929
987
  select: { role_id: true },
@@ -1039,6 +1097,7 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1039
1097
  if (!canAccess) {
1040
1098
  throw new common_1.ForbiddenException('Access denied to this dashboard');
1041
1099
  }
1100
+ await this.assertDashboardRoleAccess(dashboard.id, userId);
1042
1101
  const parsedWidgetId = Number(widgetId.replace(/^widget-/, ''));
1043
1102
  if (!Number.isInteger(parsedWidgetId) || parsedWidgetId <= 0) {
1044
1103
  throw new common_1.BadRequestException('Invalid widget id');
@@ -1087,9 +1146,18 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1087
1146
  user_id: userId,
1088
1147
  },
1089
1148
  });
1090
- const hasAccess = Boolean(dashboardUser);
1149
+ if (!dashboardUser) {
1150
+ return {
1151
+ hasAccess: false,
1152
+ accessStatus: 'not-shared',
1153
+ dashboard: null,
1154
+ };
1155
+ }
1156
+ const roleAccess = await this.getDashboardRoleAccessState(dashboard.id, userId);
1157
+ const hasAccess = roleAccess.hasRequiredRoles;
1091
1158
  return {
1092
1159
  hasAccess,
1160
+ accessStatus: roleAccess.accessStatus,
1093
1161
  dashboard: hasAccess
1094
1162
  ? {
1095
1163
  id: dashboard.id,
@@ -1337,6 +1405,7 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1337
1405
  }
1338
1406
  async getDashboardShares(userId, slug, locale) {
1339
1407
  const dashboardUser = await this.getDashboardUserOrThrow(userId, slug, locale);
1408
+ const dashboardRoleRequirements = await this.getDashboardComponentRoleRequirements(dashboardUser.dashboard_id);
1340
1409
  const sharedUsers = await this.prismaService.dashboard_user.findMany({
1341
1410
  where: {
1342
1411
  dashboard_id: dashboardUser.dashboard_id,
@@ -1346,6 +1415,11 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1346
1415
  select: {
1347
1416
  id: true,
1348
1417
  name: true,
1418
+ role_user: {
1419
+ select: {
1420
+ role_id: true,
1421
+ },
1422
+ },
1349
1423
  user_identifier: {
1350
1424
  where: {
1351
1425
  type: 'email',
@@ -1364,55 +1438,78 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1364
1438
  });
1365
1439
  return sharedUsers.map((sharedDashboardUser) => {
1366
1440
  var _a, _b;
1367
- return ({
1441
+ const userRoleIds = sharedDashboardUser.user.role_user.map((roleUser) => roleUser.role_id);
1442
+ const hasRequiredRoles = this.userHasRequiredRolesForDashboard(dashboardRoleRequirements, userRoleIds);
1443
+ return {
1368
1444
  id: sharedDashboardUser.user.id,
1369
1445
  name: sharedDashboardUser.user.name,
1370
1446
  email: (_b = (_a = sharedDashboardUser.user.user_identifier[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null,
1371
1447
  isCurrentUser: sharedDashboardUser.user.id === userId,
1372
1448
  isHome: sharedDashboardUser.is_home,
1373
- });
1449
+ hasRequiredRoles,
1450
+ accessStatus: hasRequiredRoles ? 'allowed' : 'missing-roles',
1451
+ };
1374
1452
  });
1375
1453
  }
1376
- async getShareableUsers(userId, slug, search, locale) {
1454
+ async getShareableUsers(userId, slug, options, locale) {
1455
+ var _a, _b;
1377
1456
  const dashboardUser = await this.getDashboardUserOrThrow(userId, slug, locale);
1378
- const normalizedSearch = this.toNullableString(search);
1379
- const existingUsers = await this.prismaService.dashboard_user.findMany({
1380
- where: {
1381
- dashboard_id: dashboardUser.dashboard_id,
1382
- },
1383
- select: {
1384
- user_id: true,
1385
- },
1386
- });
1387
- const users = await this.prismaService.user.findMany({
1388
- where: Object.assign({ id: {
1389
- notIn: existingUsers.map((item) => item.user_id),
1390
- } }, (normalizedSearch
1391
- ? {
1392
- OR: [
1393
- {
1394
- name: {
1395
- contains: normalizedSearch,
1396
- mode: 'insensitive',
1397
- },
1457
+ const normalizedSearch = this.toNullableString(options === null || options === void 0 ? void 0 : options.search);
1458
+ const requestedPage = Number.parseInt(String((_a = options === null || options === void 0 ? void 0 : options.page) !== null && _a !== void 0 ? _a : '1'), 10);
1459
+ const requestedPageSize = Number.parseInt(String((_b = options === null || options === void 0 ? void 0 : options.pageSize) !== null && _b !== void 0 ? _b : '10'), 10);
1460
+ const page = Number.isFinite(requestedPage) && requestedPage > 0 ? requestedPage : 1;
1461
+ const pageSize = Number.isFinite(requestedPageSize) && requestedPageSize > 0
1462
+ ? Math.min(requestedPageSize, 50)
1463
+ : 10;
1464
+ const [existingUsers, dashboardRoleRequirements] = await Promise.all([
1465
+ this.prismaService.dashboard_user.findMany({
1466
+ where: {
1467
+ dashboard_id: dashboardUser.dashboard_id,
1468
+ },
1469
+ select: {
1470
+ user_id: true,
1471
+ },
1472
+ }),
1473
+ this.getDashboardComponentRoleRequirements(dashboardUser.dashboard_id),
1474
+ ]);
1475
+ const where = Object.assign({ id: {
1476
+ notIn: existingUsers.map((item) => item.user_id),
1477
+ } }, (normalizedSearch
1478
+ ? {
1479
+ OR: [
1480
+ {
1481
+ name: {
1482
+ contains: normalizedSearch,
1483
+ mode: 'insensitive',
1398
1484
  },
1399
- {
1400
- user_identifier: {
1401
- some: {
1402
- type: 'email',
1403
- value: {
1404
- contains: normalizedSearch,
1405
- mode: 'insensitive',
1406
- },
1485
+ },
1486
+ {
1487
+ user_identifier: {
1488
+ some: {
1489
+ type: 'email',
1490
+ value: {
1491
+ contains: normalizedSearch,
1492
+ mode: 'insensitive',
1407
1493
  },
1408
1494
  },
1409
1495
  },
1410
- ],
1411
- }
1412
- : {})),
1496
+ },
1497
+ ],
1498
+ }
1499
+ : {}));
1500
+ const total = await this.prismaService.user.count({ where });
1501
+ const lastPage = total > 0 ? Math.ceil(total / pageSize) : 1;
1502
+ const safePage = Math.min(page, lastPage);
1503
+ const users = await this.prismaService.user.findMany({
1504
+ where,
1413
1505
  select: {
1414
1506
  id: true,
1415
1507
  name: true,
1508
+ role_user: {
1509
+ select: {
1510
+ role_id: true,
1511
+ },
1512
+ },
1416
1513
  user_identifier: {
1417
1514
  where: {
1418
1515
  type: 'email',
@@ -1423,57 +1520,87 @@ let DashboardCoreService = DashboardCoreService_1 = class DashboardCoreService {
1423
1520
  take: 1,
1424
1521
  },
1425
1522
  },
1426
- take: 20,
1427
- orderBy: {
1428
- id: 'desc',
1429
- },
1430
- });
1431
- return users.map((candidateUser) => {
1432
- var _a, _b;
1433
- return ({
1434
- id: candidateUser.id,
1435
- name: candidateUser.name,
1436
- email: (_b = (_a = candidateUser.user_identifier[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null,
1437
- });
1523
+ skip: (safePage - 1) * pageSize,
1524
+ take: pageSize,
1525
+ orderBy: [{ name: 'asc' }, { id: 'asc' }],
1438
1526
  });
1527
+ return {
1528
+ data: users.map((candidateUser) => {
1529
+ var _a, _b;
1530
+ const userRoleIds = candidateUser.role_user.map((roleUser) => roleUser.role_id);
1531
+ const hasRequiredRoles = this.userHasRequiredRolesForDashboard(dashboardRoleRequirements, userRoleIds);
1532
+ return {
1533
+ id: candidateUser.id,
1534
+ name: candidateUser.name,
1535
+ email: (_b = (_a = candidateUser.user_identifier[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null,
1536
+ hasRequiredRoles,
1537
+ accessStatus: hasRequiredRoles ? 'allowed' : 'missing-roles',
1538
+ };
1539
+ }),
1540
+ total,
1541
+ page: safePage,
1542
+ pageSize,
1543
+ lastPage,
1544
+ prev: safePage > 1 ? safePage - 1 : null,
1545
+ next: safePage < lastPage ? safePage + 1 : null,
1546
+ };
1439
1547
  }
1440
- async shareDashboard(userId, slug, sharedUserId, locale) {
1441
- if (!sharedUserId || Number.isNaN(Number(sharedUserId))) {
1548
+ async shareDashboard(userId, slug, sharedUserIds, sharedUserId, locale) {
1549
+ const requestedIds = Array.from(new Set([
1550
+ ...(Array.isArray(sharedUserIds) ? sharedUserIds : []),
1551
+ ...(sharedUserId !== undefined ? [sharedUserId] : []),
1552
+ ]
1553
+ .map((value) => Number(value))
1554
+ .filter((value) => Number.isInteger(value) && value > 0)));
1555
+ if (requestedIds.length === 0) {
1442
1556
  throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('validation.fieldRequired', locale, 'User is required.'));
1443
1557
  }
1444
1558
  const dashboardUser = await this.getDashboardUserOrThrow(userId, slug, locale);
1445
- if (sharedUserId === userId) {
1559
+ const sanitizedUserIds = requestedIds.filter((candidateUserId) => candidateUserId !== userId);
1560
+ if (sanitizedUserIds.length === 0) {
1446
1561
  throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('validation.invalidValue', locale, 'You already have access to this dashboard.'));
1447
1562
  }
1448
- const targetUser = await this.prismaService.user.findUnique({
1449
- where: { id: sharedUserId },
1450
- select: { id: true },
1563
+ const targetUsers = await this.prismaService.user.findMany({
1564
+ where: {
1565
+ id: {
1566
+ in: sanitizedUserIds,
1567
+ },
1568
+ },
1569
+ select: {
1570
+ id: true,
1571
+ },
1451
1572
  });
1452
- if (!targetUser) {
1573
+ if (targetUsers.length !== sanitizedUserIds.length) {
1453
1574
  throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('userNotFound', locale, 'User not found.'));
1454
1575
  }
1455
- const existingShare = await this.prismaService.dashboard_user.findFirst({
1576
+ const existingShares = await this.prismaService.dashboard_user.findMany({
1456
1577
  where: {
1457
1578
  dashboard_id: dashboardUser.dashboard_id,
1458
- user_id: sharedUserId,
1579
+ user_id: {
1580
+ in: sanitizedUserIds,
1581
+ },
1459
1582
  },
1460
- select: { id: true },
1461
- });
1462
- if (existingShare) {
1463
- return {
1464
- success: true,
1465
- alreadyShared: true,
1466
- };
1467
- }
1468
- await this.prismaService.dashboard_user.create({
1469
- data: {
1470
- dashboard_id: dashboardUser.dashboard_id,
1471
- user_id: sharedUserId,
1472
- is_home: false,
1583
+ select: {
1584
+ user_id: true,
1473
1585
  },
1474
1586
  });
1587
+ const alreadySharedUserIds = existingShares.map((item) => item.user_id);
1588
+ const alreadySharedSet = new Set(alreadySharedUserIds);
1589
+ const newUserIds = sanitizedUserIds.filter((candidateUserId) => !alreadySharedSet.has(candidateUserId));
1590
+ if (newUserIds.length > 0) {
1591
+ await this.prismaService.dashboard_user.createMany({
1592
+ data: newUserIds.map((candidateUserId) => ({
1593
+ dashboard_id: dashboardUser.dashboard_id,
1594
+ user_id: candidateUserId,
1595
+ is_home: false,
1596
+ })),
1597
+ });
1598
+ }
1475
1599
  return {
1476
1600
  success: true,
1601
+ sharedCount: newUserIds.length,
1602
+ sharedUserIds: newUserIds,
1603
+ alreadySharedUserIds,
1477
1604
  };
1478
1605
  }
1479
1606
  async revokeDashboardShare(userId, slug, sharedUserId, locale) {