@solidxai/core 0.1.9-beta.8 → 0.1.10-alpha.0

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 (158) hide show
  1. package/LICENSE +89 -0
  2. package/README.md +3 -1
  3. package/dist/commands/run-tests.command.d.ts +2 -0
  4. package/dist/commands/run-tests.command.d.ts.map +1 -1
  5. package/dist/commands/run-tests.command.js +49 -17
  6. package/dist/commands/run-tests.command.js.map +1 -1
  7. package/dist/controllers/action-metadata.controller.js +1 -1
  8. package/dist/controllers/action-metadata.controller.js.map +1 -1
  9. package/dist/controllers/facebook-authentication.controller.js +1 -1
  10. package/dist/controllers/facebook-authentication.controller.js.map +1 -1
  11. package/dist/controllers/google-authentication.controller.js +1 -1
  12. package/dist/controllers/google-authentication.controller.js.map +1 -1
  13. package/dist/controllers/menu-item-metadata.controller.js +1 -1
  14. package/dist/controllers/menu-item-metadata.controller.js.map +1 -1
  15. package/dist/controllers/microsoft-authentication.controller.js +1 -1
  16. package/dist/controllers/microsoft-authentication.controller.js.map +1 -1
  17. package/dist/controllers/mq-message-queue.controller.js +1 -1
  18. package/dist/controllers/mq-message-queue.controller.js.map +1 -1
  19. package/dist/controllers/mq-message.controller.js +1 -1
  20. package/dist/controllers/mq-message.controller.js.map +1 -1
  21. package/dist/controllers/user.controller.d.ts.map +1 -1
  22. package/dist/controllers/user.controller.js.map +1 -1
  23. package/dist/controllers/view-metadata.controller.js +1 -1
  24. package/dist/controllers/view-metadata.controller.js.map +1 -1
  25. package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
  26. package/dist/helpers/bootstrap.helper.js +2 -0
  27. package/dist/helpers/bootstrap.helper.js.map +1 -1
  28. package/dist/helpers/field-crud-managers/BigIntFieldCrudManager.js.map +1 -1
  29. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js.map +1 -1
  30. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  31. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
  32. package/dist/passport-strategies/facebook-oauth.strategy.d.ts +5 -3
  33. package/dist/passport-strategies/facebook-oauth.strategy.d.ts.map +1 -1
  34. package/dist/passport-strategies/facebook-oauth.strategy.js +41 -18
  35. package/dist/passport-strategies/facebook-oauth.strategy.js.map +1 -1
  36. package/dist/repository/security-rule.repository.js.map +1 -1
  37. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  38. package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
  39. package/dist/seeders/seed-data/solid-core-metadata.json +14 -3
  40. package/dist/services/authentication.service.d.ts +12 -13
  41. package/dist/services/authentication.service.d.ts.map +1 -1
  42. package/dist/services/authentication.service.js +42 -18
  43. package/dist/services/authentication.service.js.map +1 -1
  44. package/dist/services/chatter-message.service.js.map +1 -1
  45. package/dist/services/crud.service.js.map +1 -1
  46. package/dist/services/csv.service.js.map +1 -1
  47. package/dist/services/dashboard.service.js.map +1 -1
  48. package/dist/services/database/database-bootstrap.service.js.map +1 -1
  49. package/dist/services/excel.service.js.map +1 -1
  50. package/dist/services/export-transaction.service.js.map +1 -1
  51. package/dist/services/field-metadata.service.js +2 -2
  52. package/dist/services/field-metadata.service.js.map +1 -1
  53. package/dist/services/fixtures.service.js.map +1 -1
  54. package/dist/services/import-transaction.service.js.map +1 -1
  55. package/dist/services/list-of-values.service.js.map +1 -1
  56. package/dist/services/model-metadata.service.d.ts.map +1 -1
  57. package/dist/services/model-metadata.service.js +3 -13
  58. package/dist/services/model-metadata.service.js.map +1 -1
  59. package/dist/services/module-metadata.service.js.map +1 -1
  60. package/dist/services/queues/database-publisher.service.js +3 -3
  61. package/dist/services/queues/database-publisher.service.js.map +1 -1
  62. package/dist/services/queues/database-subscriber.service.js +3 -3
  63. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  64. package/dist/services/queues/rabbitmq-publisher.service.js +3 -3
  65. package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -1
  66. package/dist/services/queues/rabbitmq-subscriber.service.js +4 -4
  67. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  68. package/dist/services/queues/redis-publisher.service.d.ts.map +1 -1
  69. package/dist/services/queues/redis-publisher.service.js +4 -1
  70. package/dist/services/queues/redis-publisher.service.js.map +1 -1
  71. package/dist/services/queues/redis-subscriber.service.d.ts.map +1 -1
  72. package/dist/services/queues/redis-subscriber.service.js +4 -1
  73. package/dist/services/queues/redis-subscriber.service.js.map +1 -1
  74. package/dist/services/role-metadata.service.js.map +1 -1
  75. package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
  76. package/dist/services/settings/default-settings-provider.service.d.ts +74 -8
  77. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  78. package/dist/services/settings/default-settings-provider.service.js +96 -16
  79. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  80. package/dist/services/sms/TwilioSMSService.js.map +1 -1
  81. package/dist/services/solid-introspect.service.js.map +1 -1
  82. package/dist/services/user-activity-history.service.js.map +1 -1
  83. package/dist/services/user.service.d.ts +10 -8
  84. package/dist/services/user.service.d.ts.map +1 -1
  85. package/dist/services/user.service.js +85 -46
  86. package/dist/services/user.service.js.map +1 -1
  87. package/dist/services/view-metadata.service.d.ts.map +1 -1
  88. package/dist/services/view-metadata.service.js +17 -2
  89. package/dist/services/view-metadata.service.js.map +1 -1
  90. package/dist/solid-core.module.d.ts +1 -0
  91. package/dist/solid-core.module.d.ts.map +1 -1
  92. package/dist/solid-core.module.js +1 -0
  93. package/dist/solid-core.module.js.map +1 -1
  94. package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
  95. package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
  96. package/dist/subscribers/security-rule.subscriber.js.map +1 -1
  97. package/dist/subscribers/view-metadata.subscriber.js.map +1 -1
  98. package/dist/testing/core/testing-engine.js.map +1 -1
  99. package/dist/testing/reporter/webhook-reporter.d.ts +54 -0
  100. package/dist/testing/reporter/webhook-reporter.d.ts.map +1 -0
  101. package/dist/testing/reporter/webhook-reporter.js +74 -0
  102. package/dist/testing/reporter/webhook-reporter.js.map +1 -0
  103. package/package.json +6 -2
  104. package/src/commands/run-tests.command.ts +45 -17
  105. package/src/controllers/action-metadata.controller.ts +1 -1
  106. package/src/controllers/facebook-authentication.controller.ts +1 -1
  107. package/src/controllers/google-authentication.controller.ts +1 -1
  108. package/src/controllers/menu-item-metadata.controller.ts +1 -1
  109. package/src/controllers/microsoft-authentication.controller.ts +1 -1
  110. package/src/controllers/mq-message-queue.controller.ts +1 -1
  111. package/src/controllers/mq-message.controller.ts +1 -1
  112. package/src/controllers/user.controller.ts +16 -16
  113. package/src/controllers/view-metadata.controller.ts +1 -1
  114. package/src/helpers/bootstrap.helper.ts +3 -0
  115. package/src/helpers/field-crud-managers/BigIntFieldCrudManager.ts +1 -1
  116. package/src/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.ts +1 -1
  117. package/src/helpers/module-metadata-helper.service.ts +1 -1
  118. package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +1 -1
  119. package/src/passport-strategies/facebook-oauth.strategy.ts +82 -31
  120. package/src/repository/security-rule.repository.ts +1 -1
  121. package/src/seeders/module-metadata-seeder.service.ts +4 -4
  122. package/src/seeders/permission-metadata-seeder.service.ts +1 -1
  123. package/src/seeders/seed-data/solid-core-metadata.json +14 -3
  124. package/src/services/authentication.service.ts +215 -151
  125. package/src/services/chatter-message.service.ts +1 -1
  126. package/src/services/crud.service.ts +3 -3
  127. package/src/services/csv.service.ts +1 -1
  128. package/src/services/dashboard.service.ts +1 -1
  129. package/src/services/database/database-bootstrap.service.ts +1 -1
  130. package/src/services/excel.service.ts +1 -1
  131. package/src/services/export-transaction.service.ts +2 -2
  132. package/src/services/field-metadata.service.ts +3 -3
  133. package/src/services/fixtures.service.ts +2 -2
  134. package/src/services/import-transaction.service.ts +2 -2
  135. package/src/services/list-of-values.service.ts +1 -1
  136. package/src/services/model-metadata.service.ts +22 -21
  137. package/src/services/module-metadata.service.ts +7 -7
  138. package/src/services/queues/database-publisher.service.ts +4 -4
  139. package/src/services/queues/database-subscriber.service.ts +7 -7
  140. package/src/services/queues/rabbitmq-publisher.service.ts +7 -7
  141. package/src/services/queues/rabbitmq-subscriber.service.ts +13 -13
  142. package/src/services/queues/redis-publisher.service.ts +7 -4
  143. package/src/services/queues/redis-subscriber.service.ts +9 -6
  144. package/src/services/role-metadata.service.ts +1 -1
  145. package/src/services/scheduled-jobs/scheduler.service.ts +5 -5
  146. package/src/services/settings/default-settings-provider.service.ts +101 -21
  147. package/src/services/sms/TwilioSMSService.ts +2 -2
  148. package/src/services/solid-introspect.service.ts +2 -2
  149. package/src/services/user-activity-history.service.ts +1 -1
  150. package/src/services/user.service.ts +149 -77
  151. package/src/services/view-metadata.service.ts +25 -8
  152. package/src/solid-core.module.ts +1 -0
  153. package/src/subscribers/computed-entity-field.subscriber.ts +1 -1
  154. package/src/subscribers/security-rule.subscriber.ts +8 -8
  155. package/src/subscribers/view-metadata.subscriber.ts +1 -1
  156. package/src/testing/core/testing-engine.ts +2 -2
  157. package/src/testing/reporter/webhook-reporter.ts +116 -0
  158. package/dev-grooming-docs/ozzy-prompts.txt +0 -70
@@ -23,9 +23,27 @@ const getSolidCoreSettings = (isProd: boolean) =>
23
23
  sortOrder: 50,
24
24
  controlType: "boolean",
25
25
  },
26
- { moduleName: "solid-core", key: "iamFacebookOAuthEnabled", value: false, level: SettingLevel.SystemAdminEditable },
27
- { moduleName: "solid-core", key: "iamMicrosoftOAuthEnabled", value: false, level: SettingLevel.SystemAdminEditable },
28
- {
26
+ {
27
+ moduleName: "solid-core",
28
+ key: "iamFacebookOAuthEnabled",
29
+ value: false,
30
+ level: SettingLevel.SystemAdminEditable,
31
+ label: "Allow Login / Signup With Facebook",
32
+ group: "authentication-settings",
33
+ sortOrder: 50,
34
+ controlType: "boolean",
35
+ },
36
+ {
37
+ moduleName: "solid-core",
38
+ key: "iamMicrosoftOAuthEnabled",
39
+ value: false,
40
+ level: SettingLevel.SystemAdminEditable,
41
+ label: "Allow Login / Signup With Microsoft",
42
+ group: "authentication-settings",
43
+ sortOrder: 50,
44
+ controlType: "boolean",
45
+ },
46
+ {
29
47
  moduleName: "solid-core",
30
48
  key: "authPagesLayout",
31
49
  value: "center",
@@ -277,11 +295,12 @@ const getSolidCoreSettings = (isProd: boolean) =>
277
295
  level: SettingLevel.SystemAdminEditable,
278
296
  encrypted: true,
279
297
  label: "AI Code Builder Configuration",
280
- description: "Structured configuration for AI providers and model assignments.",
298
+ description:
299
+ "Structured configuration for AI providers and model assignments.",
281
300
  group: "ai-settings",
282
301
  sortOrder: 10,
283
302
  controlType: "custom",
284
- settingsWidget: "solidXGenAiCodeBuilderConfigWidget"
303
+ settingsWidget: "solidXGenAiCodeBuilderConfigWidget",
285
304
  },
286
305
  {
287
306
  moduleName: "solid-core",
@@ -616,19 +635,63 @@ const getSolidCoreSettings = (isProd: boolean) =>
616
635
  controlType: "shortText",
617
636
  },
618
637
 
619
- // facebook-oauth-settings-provider.service.ts
620
- { moduleName: "solid-core", key: "FACEBOOK_CLIENT_ID", value: process.env.IAM_FACEBOOK_OAUTH_CLIENT_ID, level: SettingLevel.SystemAdminReadonly },
621
- { moduleName: "solid-core", key: "FACEBOOK_CLIENT_SECRET", value: process.env.IAM_FACEBOOK_OAUTH_CLIENT_SECRET, level: SettingLevel.SystemEnv },
622
- { moduleName: "solid-core", key: "FACEBOOK_CALLBACK_URL", value: process.env.IAM_FACEBOOK_OAUTH_CALLBACK_URL, level: SettingLevel.SystemAdminReadonly },
623
- { moduleName: "solid-core", key: "FACEBOOK_REDIRECT_URL", value: process.env.IAM_FACEBOOK_OAUTH_REDIRECT_URL, level: SettingLevel.SystemAdminReadonly },
638
+ // facebook-oauth-settings-provider.service.ts
639
+ {
640
+ moduleName: "solid-core",
641
+ key: "FACEBOOK_CLIENT_ID",
642
+ value: process.env.IAM_FACEBOOK_OAUTH_CLIENT_ID,
643
+ level: SettingLevel.SystemAdminReadonly,
644
+ },
645
+ {
646
+ moduleName: "solid-core",
647
+ key: "FACEBOOK_CLIENT_SECRET",
648
+ value: process.env.IAM_FACEBOOK_OAUTH_CLIENT_SECRET,
649
+ level: SettingLevel.SystemEnv,
650
+ },
651
+ {
652
+ moduleName: "solid-core",
653
+ key: "FACEBOOK_CALLBACK_URL",
654
+ value: process.env.IAM_FACEBOOK_OAUTH_CALLBACK_URL,
655
+ level: SettingLevel.SystemAdminReadonly,
656
+ },
657
+ {
658
+ moduleName: "solid-core",
659
+ key: "FACEBOOK_REDIRECT_URL",
660
+ value: process.env.IAM_FACEBOOK_OAUTH_REDIRECT_URL,
661
+ level: SettingLevel.SystemAdminReadonly,
662
+ },
624
663
 
625
-
626
- // microsoft-oauth-settings-provider.service.ts
627
- { moduleName: "solid-core", key: "MICROSOFT_CLIENT_ID", value: process.env.IAM_MICROSOFT_OAUTH_CLIENT_ID, level: SettingLevel.SystemAdminReadonly },
628
- { moduleName: "solid-core", key: "MICROSOFT_CLIENT_SECRET", value: process.env.IAM_MICROSOFT_OAUTH_CLIENT_SECRET, level: SettingLevel.SystemEnv },
629
- { moduleName: "solid-core", key: "MICROSOFT_TENANT_ID", value: process.env.IAM_MICROSOFT_OAUTH_TENANT_ID || 'common', level: SettingLevel.SystemAdminReadonly },
630
- { moduleName: "solid-core", key: "MICROSOFT_CALLBACK_URL", value: process.env.IAM_MICROSOFT_OAUTH_CALLBACK_URL, level: SettingLevel.SystemAdminReadonly },
631
- { moduleName: "solid-core", key: "MICROSOFT_REDIRECT_URL", value: process.env.IAM_MICROSOFT_OAUTH_REDIRECT_URL, level: SettingLevel.SystemAdminReadonly },
664
+ // microsoft-oauth-settings-provider.service.ts
665
+ {
666
+ moduleName: "solid-core",
667
+ key: "MICROSOFT_CLIENT_ID",
668
+ value: process.env.IAM_MICROSOFT_OAUTH_CLIENT_ID,
669
+ level: SettingLevel.SystemAdminReadonly,
670
+ },
671
+ {
672
+ moduleName: "solid-core",
673
+ key: "MICROSOFT_CLIENT_SECRET",
674
+ value: process.env.IAM_MICROSOFT_OAUTH_CLIENT_SECRET,
675
+ level: SettingLevel.SystemEnv,
676
+ },
677
+ {
678
+ moduleName: "solid-core",
679
+ key: "MICROSOFT_TENANT_ID",
680
+ value: process.env.IAM_MICROSOFT_OAUTH_TENANT_ID || "common",
681
+ level: SettingLevel.SystemAdminReadonly,
682
+ },
683
+ {
684
+ moduleName: "solid-core",
685
+ key: "MICROSOFT_CALLBACK_URL",
686
+ value: process.env.IAM_MICROSOFT_OAUTH_CALLBACK_URL,
687
+ level: SettingLevel.SystemAdminReadonly,
688
+ },
689
+ {
690
+ moduleName: "solid-core",
691
+ key: "MICROSOFT_REDIRECT_URL",
692
+ value: process.env.IAM_MICROSOFT_OAUTH_REDIRECT_URL,
693
+ level: SettingLevel.SystemAdminReadonly,
694
+ },
632
695
 
633
696
  // iam-settings-provider.service.ts
634
697
  {
@@ -661,7 +724,11 @@ const getSolidCoreSettings = (isProd: boolean) =>
661
724
  label: "Registration Validation Type",
662
725
  group: "authentication-settings",
663
726
  sortOrder: 30,
664
- controlType: "shortText",
727
+ controlType: "selectionStatic",
728
+ options: [
729
+ { label: "Email", value: "email" },
730
+ { label: "Mobile", value: "mobile" },
731
+ ],
665
732
  },
666
733
  {
667
734
  moduleName: "solid-core",
@@ -671,7 +738,12 @@ const getSolidCoreSettings = (isProd: boolean) =>
671
738
  label: "Login Validation Type",
672
739
  group: "authentication-settings",
673
740
  sortOrder: 40,
674
- controlType: "shortText",
741
+ controlType: "selectionStatic",
742
+ options: [
743
+ { label: "Email", value: "email" },
744
+ { label: "Mobile", value: "mobile" },
745
+ { label: "Selectable", value: "selectable" },
746
+ ],
675
747
  },
676
748
  {
677
749
  moduleName: "solid-core",
@@ -836,7 +908,11 @@ const getSolidCoreSettings = (isProd: boolean) =>
836
908
  (
837
909
  process.env.IAM_SEND_WELCOME_EMAIL_ON_SIGNUP ?? "false"
838
910
  ).toLowerCase() === "true",
839
- level: SettingLevel.SystemEnv,
911
+ level: SettingLevel.SystemAdminEditable,
912
+ label: "Send Welcome Email On Signup",
913
+ group: "authentication-settings",
914
+ sortOrder: 180,
915
+ controlType: "boolean",
840
916
  },
841
917
  {
842
918
  moduleName: "solid-core",
@@ -845,7 +921,11 @@ const getSolidCoreSettings = (isProd: boolean) =>
845
921
  (
846
922
  process.env.IAM_SEND_WELCOME_SMS_ON_SIGNUP ?? "false"
847
923
  ).toLowerCase() === "true",
848
- level: SettingLevel.SystemEnv,
924
+ level: SettingLevel.SystemAdminEditable,
925
+ label: "Send Welcome SMS On Signup",
926
+ group: "authentication-settings",
927
+ sortOrder: 190,
928
+ controlType: "boolean",
849
929
  },
850
930
  {
851
931
  moduleName: "solid-core",
@@ -69,7 +69,7 @@ export class TwilioSMSService implements ISMS {
69
69
  try {
70
70
  const bodyTemplate = Handlebars.compile(smsTemplate.body);
71
71
  body = bodyTemplate(templateParams);
72
- } catch (error) {
72
+ } catch (error: any) {
73
73
  throw new Error('Unable to compile sms template body');
74
74
  }
75
75
  // Finally send the email.
@@ -110,7 +110,7 @@ export class TwilioSMSService implements ISMS {
110
110
  }
111
111
 
112
112
  return r;
113
- } catch (error) {
113
+ } catch (error: any) {
114
114
  throw new Error(error);
115
115
  }
116
116
  }
@@ -197,7 +197,7 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
197
197
  let ds: DataSource | undefined;
198
198
  try {
199
199
  ds = this.moduleRef.get<DataSource>(token, { strict: false });
200
- } catch (err) {
200
+ } catch (err: any) {
201
201
  this.logger.warn(`DataSource token for "${dsName ?? 'default'}" not found: ${err?.message ?? err}`);
202
202
  }
203
203
  if (!ds) {
@@ -209,7 +209,7 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
209
209
  if (!ds.isInitialized) {
210
210
  try {
211
211
  await ds.initialize(); // only if you need to initialize here; in many apps datasources are created earlier
212
- } catch (err) {
212
+ } catch (err: any) {
213
213
  this.logger.error(`Failed to initialize DataSource "${dsName}": ${err}`);
214
214
  continue;
215
215
  }
@@ -38,7 +38,7 @@ export class UserActivityHistoryService extends CRUDService<UserActivityHistory>
38
38
  ipAddress: ip,
39
39
  userAgent,
40
40
  });
41
- } catch (err) {
41
+ } catch (err: any) {
42
42
  this._logger.warn(`Failed to log event "${event}" for user ${user?.id}: ${err}`);
43
43
  }
44
44
  }
@@ -1,37 +1,68 @@
1
- import { BadRequestException, forwardRef, Inject, Injectable } from '@nestjs/common';
1
+ import {
2
+ BadRequestException,
3
+ forwardRef,
4
+ Inject,
5
+ Injectable,
6
+ } from "@nestjs/common";
2
7
  import { ModuleRef } from "@nestjs/core";
3
- import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
4
- import { CRUDService } from 'src/services/crud.service';
5
- import { EntityManager, Repository } from 'typeorm';
8
+ import { InjectEntityManager, InjectRepository } from "@nestjs/typeorm";
9
+ import { CRUDService } from "src/services/crud.service";
10
+ import { EntityManager, Repository } from "typeorm";
6
11
  import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
7
12
 
8
-
9
- import { OauthUserDto } from '../dtos/oauth-user-dto';
10
- import { RoleMetadata } from '../entities/role-metadata.entity';
11
- import { User } from '../entities/user.entity';
12
- import { ActiveUserData } from '../interfaces/active-user-data.interface';
13
- import { ERROR_MESSAGES } from 'src/constants/error-messages';
14
- import { UserRepository } from 'src/repository/user.repository';
15
- import { RoleMetadataRepository } from 'src/repository/role-metadata.repository';
16
- import { HashingService } from './hashing.service';
13
+ import { OauthUserDto } from "../dtos/oauth-user-dto";
14
+ import { RoleMetadata } from "../entities/role-metadata.entity";
15
+ import { User } from "../entities/user.entity";
16
+ import { ActiveUserData } from "../interfaces/active-user-data.interface";
17
+ import { ERROR_MESSAGES } from "src/constants/error-messages";
18
+ import { UserRepository } from "src/repository/user.repository";
19
+ import { RoleMetadataRepository } from "src/repository/role-metadata.repository";
20
+ import { HashingService } from "./hashing.service";
17
21
 
18
22
  @Injectable()
19
23
  export class UserService extends CRUDService<User> {
24
+ private buildFacebookUsernameBase(name?: string): string {
25
+ const normalized = (name || "")
26
+ .trim()
27
+ .toLowerCase()
28
+ .replace(/[^a-z0-9]+/g, "_")
29
+ .replace(/^_+|_+$/g, "");
30
+ return normalized || "facebook_user";
31
+ }
32
+
33
+ private async resolveUniqueUsername(
34
+ preferredUsername: string,
35
+ // fallbackUsername: string,
36
+ ): Promise<string> {
37
+ let candidate = preferredUsername;
38
+ let suffix = 0;
39
+
40
+ while (await this.repo.findOne({ where: { username: candidate } })) {
41
+ suffix += 1;
42
+ candidate = `${preferredUsername}_${suffix}`;
43
+ }
44
+
45
+ if (candidate) {
46
+ return candidate;
47
+ }
48
+
49
+ // return fallbackUsername;
50
+ }
51
+
20
52
  constructor(
21
53
  readonly hashingService: HashingService,
22
54
  @InjectEntityManager()
23
55
  readonly entityManager: EntityManager,
24
56
  // @InjectRepository(User, 'default')
25
57
  readonly repo: UserRepository,
26
- @InjectRepository(User, 'default')
58
+ @InjectRepository(User, "default")
27
59
  readonly nonSecurityRuleAwareRepo: Repository<User>,
28
60
  // @InjectRepository(RoleMetadata)
29
61
  // private readonly roleRepository: Repository<RoleMetadata>,
30
62
  private readonly roleRepository: RoleMetadataRepository,
31
63
  readonly moduleRef: ModuleRef,
32
-
33
64
  ) {
34
- super(entityManager, repo, 'user', 'solid-core', moduleRef);
65
+ super(entityManager, repo, "user", "solid-core", moduleRef);
35
66
  }
36
67
 
37
68
  override async delete(id: number, solidRequestContext: any = {}) {
@@ -44,13 +75,19 @@ export class UserService extends CRUDService<User> {
44
75
  return super.delete(id, solidRequestContext);
45
76
  }
46
77
 
47
- override async deleteMany(ids: number[], solidRequestContext: any = {}): Promise<any> {
78
+ override async deleteMany(
79
+ ids: number[],
80
+ solidRequestContext: any = {},
81
+ ): Promise<any> {
48
82
  if (!ids || ids.length === 0) {
49
83
  throw new Error(ERROR_MESSAGES.DELETE_IDS_REQUIRED);
50
84
  }
51
85
 
52
86
  // ❌ If the active user is trying to delete themselves
53
- if (solidRequestContext?.activeUser?.sub && ids.includes(solidRequestContext.activeUser.id)) {
87
+ if (
88
+ solidRequestContext?.activeUser?.sub &&
89
+ ids.includes(solidRequestContext.activeUser.id)
90
+ ) {
54
91
  throw new BadRequestException(ERROR_MESSAGES.DELETE_SELF_NOT_ALLOWED);
55
92
  }
56
93
 
@@ -60,9 +97,9 @@ export class UserService extends CRUDService<User> {
60
97
  async findOneByEmail(email: string): Promise<User> {
61
98
  return await this.repo.findOne({
62
99
  where: {
63
- email: email
100
+ email: email,
64
101
  },
65
- relations: {}
102
+ relations: {},
66
103
  });
67
104
  // if (!entity) {
68
105
  // throw new NotFoundException(`user with email #${email} not found`);
@@ -73,18 +110,18 @@ export class UserService extends CRUDService<User> {
73
110
  async findOneByAccessCode(accessCode: string): Promise<User> {
74
111
  return await this.repo.findOne({
75
112
  where: {
76
- accessCode: accessCode
113
+ accessCode: accessCode,
77
114
  },
78
- relations: {}
115
+ relations: {},
79
116
  });
80
117
  }
81
118
 
82
119
  async findOneByUsername(username: string): Promise<User> {
83
120
  return await this.repo.findOne({
84
121
  where: {
85
- username: username
122
+ username: username,
86
123
  },
87
- relations: {}
124
+ relations: {},
88
125
  });
89
126
  // if (!entity) {
90
127
  // throw new NotFoundException(`user with username ${username} not found`);
@@ -96,8 +133,8 @@ export class UserService extends CRUDService<User> {
96
133
  const user = await this.repo.findOne({
97
134
  where: { id: id },
98
135
  relations: {
99
- roles: true
100
- }
136
+ roles: true,
137
+ },
101
138
  });
102
139
  if (!user) {
103
140
  throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
@@ -113,21 +150,22 @@ export class UserService extends CRUDService<User> {
113
150
  const user = await this.repo.findOne({
114
151
  where: { username: username },
115
152
  relations: {
116
- roles: true
117
- }
153
+ roles: true,
154
+ },
118
155
  });
119
156
  if (!user) {
120
157
  throw new Error(ERROR_MESSAGES.USER_NOT_FOUND_BY_USERNAME(username));
121
158
  }
122
- const role = await this.roleRepository.findOne({ where: { name: roleName } });
159
+ const role = await this.roleRepository.findOne({
160
+ where: { name: roleName },
161
+ });
123
162
  if (!role) {
124
163
  throw new Error(ERROR_MESSAGES.ROLE_NOT_FOUND(roleName));
125
164
  }
126
165
 
127
166
  if (user.roles && user.roles.length > 0) {
128
167
  user.roles.push(role);
129
- }
130
- else {
168
+ } else {
131
169
  user.roles = [role];
132
170
  }
133
171
 
@@ -137,7 +175,7 @@ export class UserService extends CRUDService<User> {
137
175
  async addRolesToUser(username: string, roleNames: string[]): Promise<User> {
138
176
  const user = await this.nonSecurityRuleAwareRepo.findOne({
139
177
  where: { username: username },
140
- relations: { roles: true }
178
+ relations: { roles: true },
141
179
  });
142
180
 
143
181
  if (!user) {
@@ -145,43 +183,47 @@ export class UserService extends CRUDService<User> {
145
183
  }
146
184
 
147
185
  const roles = await this.roleRepository.find({
148
- where: roleNames.map(roleName => ({ name: roleName }))
186
+ where: roleNames.map((roleName) => ({ name: roleName })),
149
187
  });
150
188
 
151
189
  if (roles.length !== roleNames.length) {
152
- const foundRoleNames = roles.map(role => role.name);
153
- const missingRoles = roleNames.filter(roleName => !foundRoleNames.includes(roleName));
190
+ const foundRoleNames = roles.map((role) => role.name);
191
+ const missingRoles = roleNames.filter(
192
+ (roleName) => !foundRoleNames.includes(roleName),
193
+ );
154
194
  throw new Error(ERROR_MESSAGES.ROLES_NOT_FOUND(missingRoles));
155
195
  }
156
196
 
157
- const currentRoles = user.roles.map(role => role.name);
197
+ const currentRoles = user.roles.map((role) => role.name);
158
198
 
159
- const rolesToAdd = roles.filter(role => !currentRoles.includes(role.name));
199
+ const rolesToAdd = roles.filter(
200
+ (role) => !currentRoles.includes(role.name),
201
+ );
160
202
 
161
- const rolesToRemove = user.roles.filter(role => !roleNames.includes(role.name));
203
+ const rolesToRemove = user.roles.filter(
204
+ (role) => !roleNames.includes(role.name),
205
+ );
162
206
 
163
207
  if (rolesToAdd.length > 0) {
164
208
  user.roles.push(...rolesToAdd);
165
209
  }
166
210
 
167
211
  if (rolesToRemove.length > 0) {
168
- user.roles = user.roles.filter(role => !rolesToRemove.includes(role));
212
+ user.roles = user.roles.filter((role) => !rolesToRemove.includes(role));
169
213
  }
170
214
 
171
215
  return await this.nonSecurityRuleAwareRepo.save(user);
172
216
  }
173
217
 
174
-
175
218
  async removeRoleFromUser(username: string, roleName: string): Promise<User> {
176
-
177
219
  // load the role with the respective permissions.
178
220
  const user = await this.repo.findOne({
179
221
  where: {
180
- username: username
222
+ username: username,
181
223
  },
182
224
  relations: {
183
- roles: true
184
- }
225
+ roles: true,
226
+ },
185
227
  });
186
228
 
187
229
  if (!user) {
@@ -189,7 +231,7 @@ export class UserService extends CRUDService<User> {
189
231
  }
190
232
 
191
233
  // modify the permissions array.
192
- user.roles = user.roles.filter(role => role.name !== roleName);
234
+ user.roles = user.roles.filter((role) => role.name !== roleName);
193
235
 
194
236
  return await this.repo.save(user);
195
237
  }
@@ -201,11 +243,11 @@ export class UserService extends CRUDService<User> {
201
243
  email: oauthUserDto.email,
202
244
  },
203
245
  relations: {
204
- roles: true
205
- }
246
+ roles: true,
247
+ },
206
248
  });
207
249
 
208
- // if we are unable to find a user then we need to create one.
250
+ // if we are unable to find a user then we need to create one.
209
251
  if (!user) {
210
252
  const user = new User();
211
253
  user.username = oauthUserDto.email;
@@ -220,9 +262,12 @@ export class UserService extends CRUDService<User> {
220
262
  const savedUser = await this.repo.save(user);
221
263
 
222
264
  // Initialize the user roles
223
- await this.initializeRolesForNewUser([this.settingService.getConfigValue<SolidCoreSetting>('defaultRole')], savedUser);
265
+ await this.initializeRolesForNewUser(
266
+ [this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
267
+ savedUser,
268
+ );
224
269
  }
225
- // else we update the user and store the generated code & access token.
270
+ // else we update the user and store the generated code & access token.
226
271
  else {
227
272
  const entity = await this.repo.preload({
228
273
  id: user.id,
@@ -240,19 +285,48 @@ export class UserService extends CRUDService<User> {
240
285
  }
241
286
 
242
287
  async resolveUserOnOauthFacebook(oauthUserDto: OauthUserDto): Promise<User> {
243
- const user = await this.repo.findOne({
244
- where: {
245
- email: oauthUserDto.email,
246
- },
247
- relations: {
248
- roles: true,
249
- },
250
- });
288
+ const normalizedEmail = oauthUserDto.email?.trim().toLowerCase() || null;
289
+ let user: User | null = null;
290
+
291
+ if (oauthUserDto.providerId) {
292
+ user = await this.repo.findOne({
293
+ where: {
294
+ facebookId: oauthUserDto.providerId,
295
+ },
296
+ relations: {
297
+ roles: true,
298
+ },
299
+ });
300
+ }
251
301
 
252
302
  if (!user) {
303
+ const facebookProviderFallback = `facebook_${oauthUserDto.providerId}`;
304
+ const facebookNameUsername = this.buildFacebookUsernameBase(
305
+ oauthUserDto.name,
306
+ );
307
+ // let username = normalizedEmail || facebookNameUsername;
308
+ let username = facebookNameUsername;
309
+
310
+ let email = normalizedEmail;
311
+
312
+ // Avoid clashing with local users that already own the same email/username.
313
+ if (normalizedEmail) {
314
+ const existingByEmail = await this.repo.findOne({
315
+ where: { email: normalizedEmail },
316
+ });
317
+ if (existingByEmail) {
318
+ username = facebookNameUsername;
319
+ email = null;
320
+ }
321
+ }
322
+ username = await this.resolveUniqueUsername(
323
+ username,
324
+ // facebookProviderFallback,
325
+ );
326
+
253
327
  const newUser = new User();
254
- newUser.username = oauthUserDto.email;
255
- newUser.email = oauthUserDto.email;
328
+ newUser.username = username;
329
+ newUser.email = email;
256
330
  newUser.fullName = oauthUserDto.name;
257
331
  newUser.lastLoginProvider = oauthUserDto.provider;
258
332
  newUser.accessCode = oauthUserDto.accessCode;
@@ -263,7 +337,7 @@ export class UserService extends CRUDService<User> {
263
337
  const savedUser = await this.repo.save(newUser);
264
338
 
265
339
  await this.initializeRolesForNewUser(
266
- [this.settingService.getConfigValue<SolidCoreSetting>('defaultRole')],
340
+ [this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
267
341
  savedUser,
268
342
  );
269
343
  return savedUser;
@@ -276,13 +350,11 @@ export class UserService extends CRUDService<User> {
276
350
  facebookId: oauthUserDto.providerId,
277
351
  facebookProfilePicture: oauthUserDto.picture,
278
352
  });
279
-
280
353
  await this.repo.save(entity);
281
354
  return entity;
282
355
  }
283
356
  }
284
357
 
285
-
286
358
  async resolveUserOnOauthMicrosoft(oauthUserDto: OauthUserDto): Promise<User> {
287
359
  const user = await this.repo.findOne({
288
360
  where: {
@@ -307,10 +379,9 @@ export class UserService extends CRUDService<User> {
307
379
  const savedUser = await this.repo.save(newUser);
308
380
 
309
381
  await this.initializeRolesForNewUser(
310
- [this.settingService.getConfigValue<SolidCoreSetting>('defaultRole')],
382
+ [this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
311
383
  savedUser,
312
384
  );
313
- return savedUser;
314
385
  } else {
315
386
  const entity = await this.repo.preload({
316
387
  id: user.id,
@@ -322,25 +393,29 @@ export class UserService extends CRUDService<User> {
322
393
  });
323
394
 
324
395
  await this.repo.save(entity);
325
- return entity;
326
396
  }
397
+ return user;
327
398
  }
328
399
 
329
- async findUsersByRole(roleName: string, relations: any = {}): Promise<User[]> {
400
+ async findUsersByRole(
401
+ roleName: string,
402
+ relations: any = {},
403
+ ): Promise<User[]> {
330
404
  return await this.repo.find({
331
405
  where: {
332
406
  roles: {
333
- name: roleName
334
- }
407
+ name: roleName,
408
+ },
335
409
  },
336
- relations: relations
410
+ relations: relations,
337
411
  });
338
412
  }
339
413
 
340
414
  async checkIfPermissionExists(query: any, activeUser: ActiveUserData) {
341
-
342
- const matchingPermssions = activeUser.permissions.filter((p) => query.permissionNames.includes(p));
343
- return matchingPermssions
415
+ const matchingPermssions = activeUser.permissions.filter((p) =>
416
+ query.permissionNames.includes(p),
417
+ );
418
+ return matchingPermssions;
344
419
  }
345
420
 
346
421
  async initializeRolesForNewUser(roles: string[], user: User) {
@@ -348,7 +423,7 @@ export class UserService extends CRUDService<User> {
348
423
  throw new BadRequestException(ERROR_MESSAGES.USER_MISSING_ID);
349
424
  }
350
425
  let userRoles = [];
351
- // Default Internal user role assigned
426
+ // Default Internal user role assigned
352
427
  userRoles.push("Internal User");
353
428
  if (roles) {
354
429
  userRoles = [...userRoles, ...roles];
@@ -372,7 +447,4 @@ export class UserService extends CRUDService<User> {
372
447
  passwordSchemeVersion: this.hashingService.currentVersion(),
373
448
  };
374
449
  }
375
-
376
-
377
450
  }
378
-