firstly 0.0.16-next.2 → 0.1.0-next.4

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 (156) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/esm/SqlDatabase/FF_LogToConsole.d.ts +14 -0
  3. package/esm/SqlDatabase/FF_LogToConsole.js +24 -5
  4. package/esm/auth/Entities.d.ts +4 -3
  5. package/esm/auth/Entities.js +19 -15
  6. package/esm/auth/server/AuthController.server.js +86 -93
  7. package/esm/auth/server/handleAuth.d.ts +4 -2
  8. package/esm/auth/server/handleAuth.js +8 -4
  9. package/esm/auth/server/helperRole.d.ts +11 -4
  10. package/esm/auth/server/helperRole.js +29 -20
  11. package/esm/auth/server/index.d.ts +1 -0
  12. package/esm/auth/server/index.js +1 -0
  13. package/esm/auth/server/module.d.ts +27 -5
  14. package/esm/auth/server/module.js +24 -10
  15. package/esm/auth/server/providers/github.js +13 -2
  16. package/esm/auth/static/assets/Page-5iUUg7GN.d.ts +2 -0
  17. package/esm/auth/static/assets/Page-5iUUg7GN.js +1 -0
  18. package/esm/auth/static/assets/Page-BHW08QWz.css +1 -0
  19. package/esm/auth/static/assets/Page-BcSzu3xq.d.ts +2 -0
  20. package/esm/auth/static/assets/Page-BcSzu3xq.js +20 -0
  21. package/esm/auth/static/assets/Page-sEK21EQB.d.ts +2 -0
  22. package/esm/auth/static/assets/Page-sEK21EQB.js +1 -0
  23. package/esm/auth/static/assets/index-RpcgeXPm.d.ts +232 -0
  24. package/esm/auth/static/assets/index-RpcgeXPm.js +2 -0
  25. package/esm/auth/static/index.html +1 -1
  26. package/esm/auth/types.d.ts +1 -0
  27. package/esm/bin/cmd.js +16 -16
  28. package/esm/changeLog/changeLogEntities.d.ts +21 -0
  29. package/esm/changeLog/changeLogEntities.js +57 -0
  30. package/esm/changeLog/index.d.ts +3 -15
  31. package/esm/changeLog/index.js +3 -51
  32. package/esm/changeLog/server/index.d.ts +39 -28
  33. package/esm/changeLog/server/index.js +40 -29
  34. package/esm/cron/Cron.d.ts +11 -0
  35. package/esm/cron/Cron.js +43 -0
  36. package/esm/cron/Role_Cron.d.ts +3 -0
  37. package/esm/cron/Role_Cron.js +3 -0
  38. package/esm/cron/index.d.ts +3 -0
  39. package/esm/cron/index.js +3 -0
  40. package/esm/cron/server/index.d.ts +29 -11
  41. package/esm/cron/server/index.js +29 -13
  42. package/esm/feedback/FeedbackController.d.ts +3 -1
  43. package/esm/feedback/FeedbackController.js +23 -1
  44. package/esm/feedback/server/index.d.ts +2 -2
  45. package/esm/feedback/server/index.js +2 -2
  46. package/esm/feedback/types.d.ts +6 -0
  47. package/esm/feedback/ui/DialogIssue.svelte +16 -2
  48. package/esm/feedback/ui/DialogIssues.svelte +2 -2
  49. package/esm/feedback/ui/DialogMilestones.svelte +2 -3
  50. package/esm/feedback/ui/Feedback.svelte +1 -1
  51. package/esm/formats/dates.js +1 -1
  52. package/esm/formats/index.d.ts +1 -1
  53. package/esm/formats/index.js +1 -1
  54. package/esm/formats/strings.d.ts +2 -0
  55. package/esm/formats/strings.js +22 -0
  56. package/esm/index.d.ts +3 -84
  57. package/esm/index.js +3 -47
  58. package/esm/internals/FF_Entity.d.ts +2 -0
  59. package/esm/{FF_Entity.js → internals/FF_Entity.js} +10 -10
  60. package/esm/{FF_Fields.js → internals/FF_Fields.js} +1 -1
  61. package/esm/{cellsBuildor.d.ts → internals/cellsBuildor.d.ts} +1 -1
  62. package/esm/{common.d.ts → internals/common.d.ts} +0 -2
  63. package/esm/internals/common.js +3 -0
  64. package/esm/{helper.js → internals/helper.js} +1 -1
  65. package/esm/internals/index.d.ts +83 -0
  66. package/esm/internals/index.js +45 -0
  67. package/esm/{storeItem.js → internals/storeItem.js} +2 -2
  68. package/esm/mail/Mail.d.ts +13 -0
  69. package/esm/mail/Mail.js +51 -0
  70. package/esm/mail/Role_Mail.d.ts +3 -0
  71. package/esm/mail/Role_Mail.js +3 -0
  72. package/esm/mail/index.d.ts +7 -2
  73. package/esm/mail/index.js +7 -2
  74. package/esm/mail/server/formatMailHelper.d.ts +16 -0
  75. package/esm/mail/server/formatMailHelper.js +113 -0
  76. package/esm/mail/server/index.d.ts +32 -19
  77. package/esm/mail/server/index.js +113 -38
  78. package/esm/server/index.d.ts +9 -6
  79. package/esm/server/index.js +28 -50
  80. package/esm/svelte/FF_Layout.svelte +2 -2
  81. package/esm/svelte/dialog/DialogManagement.svelte +2 -2
  82. package/esm/svelte/dialog/DialogPrimitive.svelte +1 -1
  83. package/esm/svelte/dialog/DialogPrimitive.svelte.d.ts +1 -1
  84. package/esm/svelte/dialog/dialog.d.ts +1 -1
  85. package/esm/svelte/dialog/dialog.js +1 -1
  86. package/esm/svelte/helpers.d.ts +1 -0
  87. package/esm/svelte/index.d.ts +1 -0
  88. package/esm/svelte/index.js +1 -0
  89. package/esm/svelte/initRemultSvelteReactivity.d.ts +1 -0
  90. package/esm/svelte/initRemultSvelteReactivity.js +29 -0
  91. package/esm/sveltekit/server/index.d.ts +2 -2
  92. package/esm/sveltekit/server/index.js +2 -2
  93. package/esm/ui/Button.svelte +2 -2
  94. package/esm/ui/Button.svelte.d.ts +1 -1
  95. package/esm/ui/Field.svelte +7 -5
  96. package/esm/ui/Field.svelte.d.ts +5 -2
  97. package/esm/ui/FieldGroup.svelte +4 -3
  98. package/esm/ui/FieldGroup.svelte.d.ts +7 -4
  99. package/esm/ui/Grid.svelte +3 -3
  100. package/esm/ui/Grid.svelte.d.ts +2 -2
  101. package/esm/ui/Grid2.svelte +3 -3
  102. package/esm/ui/Grid2.svelte.d.ts +1 -1
  103. package/esm/ui/GridPaginate.svelte +2 -2
  104. package/esm/ui/GridPaginate2.svelte +1 -1
  105. package/esm/ui/Loading.svelte +1 -1
  106. package/esm/ui/Tooltip.svelte +1 -1
  107. package/esm/ui/dialog/DialogForm.svelte +5 -5
  108. package/esm/ui/dialog/DialogManagement.svelte +2 -2
  109. package/esm/ui/dialog/DialogPrimitive.svelte +3 -3
  110. package/esm/ui/dialog/DialogPrimitive.svelte.d.ts +1 -1
  111. package/esm/ui/dialog/FormEditAction.svelte +3 -3
  112. package/esm/ui/dialog/FormEditAction.svelte.d.ts +1 -1
  113. package/esm/ui/dialog/dialog.d.ts +1 -1
  114. package/esm/ui/dialog/dialog.js +1 -1
  115. package/esm/ui/index.d.ts +1 -1
  116. package/esm/ui/internals/FieldContainer.svelte +2 -2
  117. package/esm/ui/internals/Textarea.svelte +3 -3
  118. package/esm/ui/internals/select/MultiSelectMelt.svelte +7 -7
  119. package/esm/ui/internals/select/MultiSelectMelt.svelte.d.ts +1 -1
  120. package/esm/ui/internals/select/SelectMelt.svelte +29 -15
  121. package/esm/ui/internals/select/SelectMelt.svelte.d.ts +5 -2
  122. package/esm/ui/internals/select/SelectRadio.svelte +1 -1
  123. package/esm/ui/internals/select/SelectRadio.svelte.d.ts +1 -1
  124. package/esm/ui/link/LinkPlus.svelte +3 -3
  125. package/esm/ui/link/LinkPlus.svelte.d.ts +1 -1
  126. package/esm/virtual/Customer.js +1 -1
  127. package/esm/virtual/StateDemoEnum.d.ts +2 -2
  128. package/esm/virtual/StateDemoEnum.js +1 -1
  129. package/esm/virtual/UIEntity.js +1 -1
  130. package/esm/vite/index.js +50 -52
  131. package/package.json +19 -12
  132. package/esm/FF_Entity.d.ts +0 -2
  133. package/esm/ROUTES.d.ts +0 -102
  134. package/esm/ROUTES.js +0 -106
  135. package/esm/auth/static/assets/Page-BorYIfy9.d.ts +0 -6
  136. package/esm/auth/static/assets/Page-BorYIfy9.js +0 -1
  137. package/esm/auth/static/assets/Page-Cm4MsdIa.d.ts +0 -6
  138. package/esm/auth/static/assets/Page-Cm4MsdIa.js +0 -20
  139. package/esm/auth/static/assets/Page-CqsLm8yQ.d.ts +0 -6
  140. package/esm/auth/static/assets/Page-CqsLm8yQ.js +0 -1
  141. package/esm/auth/static/assets/Page-JfNiCSIG.css +0 -1
  142. package/esm/auth/static/assets/index-Borxa2ns.d.ts +0 -234
  143. package/esm/auth/static/assets/index-Borxa2ns.js +0 -44
  144. package/esm/common.js +0 -8
  145. package/esm/mail/templates/DefaultMail.svelte +0 -86
  146. package/esm/mail/templates/DefaultMail.svelte.d.ts +0 -30
  147. /package/esm/{BaseEnum.d.ts → internals/BaseEnum.d.ts} +0 -0
  148. /package/esm/{BaseEnum.js → internals/BaseEnum.js} +0 -0
  149. /package/esm/{FF_Fields.d.ts → internals/FF_Fields.d.ts} +0 -0
  150. /package/esm/{cellsBuildor.js → internals/cellsBuildor.js} +0 -0
  151. /package/esm/{helper.d.ts → internals/helper.d.ts} +0 -0
  152. /package/esm/{storeItem.d.ts → internals/storeItem.d.ts} +0 -0
  153. /package/esm/{storeList.d.ts → internals/storeList.d.ts} +0 -0
  154. /package/esm/{storeList.js → internals/storeList.js} +0 -0
  155. /package/esm/{theme.d.ts → internals/theme.d.ts} +0 -0
  156. /package/esm/{theme.js → internals/theme.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # firstly
2
2
 
3
+ ## 0.1.0-next.4
4
+
5
+ ### Minor Changes
6
+
7
+ - [#153](https://github.com/jycouet/firstly/pull/153)
8
+ [`f8ca698`](https://github.com/jycouet/firstly/commit/f8ca698aa2b4ec7cfe77f6e63486c0bf9a124946)
9
+ Thanks [@jycouet](https://github.com/jycouet)! - all root import ... from `firstly` moved to
10
+ `firstly/internals` (we will gradually add them back when needed
11
+
12
+ ## 0.1.0-next.3
13
+
14
+ ### Minor Changes
15
+
16
+ - [#140](https://github.com/jycouet/firstly/pull/140)
17
+ [`94b2188`](https://github.com/jycouet/firstly/commit/94b2188c78772f94e7835ab933fcebbe2a37703c)
18
+ Thanks [@jycouet](https://github.com/jycouet)! - [BREAKING] - deprecate firstly module in favor of
19
+ remult module
20
+
21
+ ```diff
22
+ remultApi({
23
+ -- modules: [], // firstly modules
24
+ ++ modulesFF: [], // firstly modules
25
+ })
26
+
27
+ Then, you can use `modules` level of `remult`
28
+ ```
29
+
30
+ ### Patch Changes
31
+
32
+ - [#117](https://github.com/jycouet/firstly/pull/117)
33
+ [`407ed4d`](https://github.com/jycouet/firstly/commit/407ed4db8f4b99f234932965b870d51f6a9c07ca)
34
+ Thanks [@jycouet](https://github.com/jycouet)! - need to pass `redirect` to handleAuth manually
35
+
36
+ - [#117](https://github.com/jycouet/firstly/pull/117)
37
+ [`f30c737`](https://github.com/jycouet/firstly/commit/f30c73781d8f50da08fcdc25f1f7611133ea8b0c)
38
+ Thanks [@jycouet](https://github.com/jycouet)! - switch mail engine to sailkit
39
+
40
+ - [#117](https://github.com/jycouet/firstly/pull/117)
41
+ [`5e1d67e`](https://github.com/jycouet/firstly/commit/5e1d67eb8f75127c3d729945e20b22c40184ee20)
42
+ Thanks [@jycouet](https://github.com/jycouet)! - [BREAKING] - Auth Identifier got removed in favor
43
+ of name in User table.
44
+
3
45
  ## 0.0.16-next.2
4
46
 
5
47
  ### Patch Changes
@@ -1,5 +1,19 @@
1
+ type TypeQuery = 'INSERT' | 'SELECT' | 'SELECT_COUNT' | 'UPDATE' | 'DELETE' | 'CREATE' | 'ALTER' | 'DROP' | 'TRUNCATE' | 'GRANT' | 'REVOKE';
2
+ /**
3
+ * @example
4
+ * SqlDatabase.LogToConsole = (...a) => FF_LogToConsole(...a, {
5
+ * type: {
6
+ * exclude: ['SELECT', 'SELECT_COUNT']
7
+ * }
8
+ * })
9
+ */
1
10
  export declare const FF_LogToConsole: (duration: number, query: string, args: Record<string, any>, options?: {
11
+ type?: {
12
+ include?: TypeQuery[];
13
+ exclude?: TypeQuery[];
14
+ };
2
15
  withDetails?: boolean;
3
16
  tablesToHide?: string[][];
4
17
  ending?: (duration: number, query: string, args: Record<string, any>, tables: string[]) => void;
5
18
  }) => string | undefined;
19
+ export {};
@@ -4,7 +4,7 @@ const typeQuery = new Map([
4
4
  // CRUD
5
5
  ['INSERT', '⚪'], // Used to insert new data into a database.
6
6
  ['SELECT', '🔵'], // Used to select data from a database and retrieve it.
7
- ['COUNT ', '🟦'], // Used to count data from a database and retrieve it.
7
+ ['SELECT_COUNT', '🟦'], // Used to count data from a database and retrieve it.
8
8
  ['UPDATE', '🟣'], // Used to update existing data within a database.
9
9
  ['DELETE', '🟤'], // Used to delete existing data from a database.
10
10
  // Additional
@@ -18,17 +18,36 @@ const typeQuery = new Map([
18
18
  const keys = ['FROM', 'WHERE', 'LIMIT', 'OFFSET'];
19
19
  const typeQueryKey = Array.from(typeQuery.keys());
20
20
  const log = new Log('');
21
+ /**
22
+ * @example
23
+ * SqlDatabase.LogToConsole = (...a) => FF_LogToConsole(...a, {
24
+ * type: {
25
+ * exclude: ['SELECT', 'SELECT_COUNT']
26
+ * }
27
+ * })
28
+ */
21
29
  export const FF_LogToConsole = (duration, query, args, options) => {
22
30
  if (duration < SqlDatabase.durationThreshold)
23
31
  return;
24
32
  const rawSql = query
33
+ .replace(/--.*?(?=\r\n|\n|$)/g, '') // Remove SQL comments
25
34
  .replace(/(\r\n|\n|\r|\t)/gm, ' ')
26
35
  .replace(/ +/g, ' ')
27
36
  .trim();
28
37
  const s = rawSql.split(' ');
29
38
  let first = s[0].toUpperCase();
30
- if (s.includes('count(*)')) {
31
- first = 'COUNT ';
39
+ if (s.length > 1 && s[1].toLowerCase() === 'count(*)') {
40
+ first = 'SELECT_COUNT';
41
+ }
42
+ if (options?.type?.include) {
43
+ if (!options.type.include.includes(first)) {
44
+ return;
45
+ }
46
+ }
47
+ if (options?.type?.exclude) {
48
+ if (options.type.exclude.includes(first)) {
49
+ return;
50
+ }
32
51
  }
33
52
  const tables = [];
34
53
  const listArgs = [];
@@ -75,8 +94,8 @@ export const FF_LogToConsole = (duration, query, args, options) => {
75
94
  ['information_Schema.tables'],
76
95
  ['information_schema.columns'],
77
96
  // ['__remult_migrations_version'],
78
- ['ff_auth.accounts'],
79
- ['ff_auth.users'],
97
+ // ['ff_auth.accounts'],
98
+ // ['ff_auth.users'],
80
99
  ['ff_auth.users_sessions'],
81
100
  ['_ff_change_logs'],
82
101
  ];
@@ -1,6 +1,6 @@
1
1
  import type { OAuth2Tokens } from 'arctic';
2
- import { BaseEnum } from '..';
3
- import type { BaseEnumOptions } from '..';
2
+ import { BaseEnum } from '../internals';
3
+ import type { BaseEnumOptions } from '../internals';
4
4
  import type { OAuth2UserInfo } from './types';
5
5
  export declare const FF_Role_Auth: {
6
6
  readonly FF_Role_Auth_Admin: "FF_Role_Auth.Admin";
@@ -10,7 +10,7 @@ export declare class FFAuthUser {
10
10
  id: string;
11
11
  createdAt: Date;
12
12
  updatedAt?: Date;
13
- identifier: string;
13
+ name: string;
14
14
  roles: string[];
15
15
  accounts: FFAuthAccount[];
16
16
  sessions: FFAuthUserSession[];
@@ -23,6 +23,7 @@ export declare class FFAuthAccount {
23
23
  user: FFAuthUser;
24
24
  provider: string;
25
25
  providerUserId: string;
26
+ email?: string | null;
26
27
  hashPassword?: string;
27
28
  token?: string;
28
29
  expiresAt?: Date;
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  var FFAuthProvider_1;
8
8
  import { Fields, Relations, Validators, ValueListFieldType } from 'remult';
9
- import { BaseEnum, FF_Entity, FF_Role } from '..';
9
+ import { BaseEnum, FF_Entity, FF_Role } from '../internals';
10
10
  export const FF_Role_Auth = {
11
11
  FF_Role_Auth_Admin: 'FF_Role_Auth.Admin',
12
12
  FF_Role_Auth_Invite: 'FF_Role_Auth.Invite',
@@ -15,7 +15,7 @@ let FFAuthUser = class FFAuthUser {
15
15
  id;
16
16
  createdAt;
17
17
  updatedAt;
18
- identifier;
18
+ name;
19
19
  roles = [];
20
20
  accounts;
21
21
  sessions;
@@ -24,29 +24,29 @@ __decorate([
24
24
  Fields.cuid()
25
25
  ], FFAuthUser.prototype, "id", void 0);
26
26
  __decorate([
27
- Fields.createdAt()
27
+ Fields.createdAt({ includeInApi: [FF_Role_Auth.FF_Role_Auth_Admin, FF_Role.FF_Role_Admin] })
28
28
  ], FFAuthUser.prototype, "createdAt", void 0);
29
29
  __decorate([
30
- Fields.updatedAt()
30
+ Fields.updatedAt({ includeInApi: [FF_Role_Auth.FF_Role_Auth_Admin, FF_Role.FF_Role_Admin] })
31
31
  ], FFAuthUser.prototype, "updatedAt", void 0);
32
32
  __decorate([
33
33
  Fields.string({
34
- allowApiUpdate: false,
35
- validate: [
36
- Validators.unique(),
37
- Validators.required(),
38
- (e) => {
39
- if (e.identifier?.length < 2)
40
- throw 'Must be at least 2 characters long';
41
- },
42
- ],
34
+ required: true,
35
+ validate: [Validators.unique(), Validators.required()],
43
36
  })
44
- ], FFAuthUser.prototype, "identifier", void 0);
37
+ ], FFAuthUser.prototype, "name", void 0);
45
38
  __decorate([
46
39
  Fields.json(() => [], {
40
+ includeInApi: [FF_Role_Auth.FF_Role_Auth_Admin, FF_Role.FF_Role_Admin],
47
41
  inputType: 'selectEnum',
48
42
  valueConverter: {
49
- toDb: (x) => (x ? x.join(',') : []),
43
+ toDb: (x) => {
44
+ if (x === null)
45
+ return null;
46
+ if (Array.isArray(x))
47
+ return x.join(',');
48
+ return x;
49
+ },
50
50
  //FIXME: refacto this + remove "permissions" & add a disable user!
51
51
  fromDb: (x) => {
52
52
  return x
@@ -80,6 +80,7 @@ let FFAuthAccount = class FFAuthAccount {
80
80
  user;
81
81
  provider = FFAuthProvider.PASSWORD.id;
82
82
  providerUserId = '';
83
+ email = null;
83
84
  hashPassword;
84
85
  token;
85
86
  expiresAt;
@@ -107,6 +108,9 @@ __decorate([
107
108
  __decorate([
108
109
  Fields.string()
109
110
  ], FFAuthAccount.prototype, "providerUserId", void 0);
111
+ __decorate([
112
+ Fields.string({ allowNull: true })
113
+ ], FFAuthAccount.prototype, "email", void 0);
110
114
  __decorate([
111
115
  Fields.string({ includeInApi: false, allowNull: true })
112
116
  ], FFAuthAccount.prototype, "hashPassword", void 0);
@@ -2,6 +2,7 @@ import { decodeHex, encodeHexLowerCase } from '@oslojs/encoding';
2
2
  import { createTOTPKeyURI, generateTOTP, verifyTOTPWithGracePeriod } from '@oslojs/otp';
3
3
  import { generateState } from 'arctic';
4
4
  import { EntityError, remult, repo } from 'remult';
5
+ import { nameify } from '../../formats/strings.js';
5
6
  import { gray, green, magenta, red, yellow } from '@kitql/helpers';
6
7
  import { FFAuthProvider } from '../Entities.js';
7
8
  import { invalidateSession } from './helperDb.js';
@@ -15,7 +16,7 @@ const getSendMail = () => {
15
16
  authModuleRaw.log.error(`Missing ${green(`remult.context`)}.${red(`sendMail`)}`);
16
17
  authModuleRaw.log.error('');
17
18
  authModuleRaw.log.error(gray('Add this to your modules:'));
18
- authModuleRaw.log.error('import { mail } from "firstly/mail/server"');
19
+ authModuleRaw.log.error('import { mail } from "../../mail/server"');
19
20
  authModuleRaw.log.error('');
20
21
  authModuleRaw.log.error('{');
21
22
  authModuleRaw.log.error(` modules: [`);
@@ -52,19 +53,24 @@ export class AuthControllerServer {
52
53
  if (accounts.length === 0) {
53
54
  throw new EntityError({ message: `Demo accounts are not enabled!` });
54
55
  }
55
- const account = accounts.find((a) => a.name === name);
56
- if (!account) {
56
+ const accountConf = accounts.find((a) => a.name === name);
57
+ if (!accountConf) {
57
58
  throw new EntityError({ message: `${name} not found as demo account!` });
58
59
  }
59
60
  const oSafe = getSafeOptions();
60
- let user = await repo(oSafe.User).findFirst({ identifier: name });
61
- if (!user) {
62
- user = repo(oSafe.User).create();
61
+ const user = await repo(oSafe.User).upsert({ where: { name } });
62
+ await repo(oSafe.Account).upsert({
63
+ where: {
64
+ provider: FFAuthProvider.DEMO.id,
65
+ providerUserId: name,
66
+ userId: user.id,
67
+ },
68
+ });
69
+ const r = mergeRoles(user.roles, accountConf.roles);
70
+ if (r.changed) {
71
+ user.roles = r.roles;
72
+ await repo(oSafe.User).save(user);
63
73
  }
64
- user.identifier = name;
65
- const r = mergeRoles(user.roles, account.roles);
66
- user.roles = r.roles;
67
- await repo(oSafe.User).save(user);
68
74
  const session = await ff_createSession(user.id);
69
75
  return {
70
76
  message: `You're in with demo account!`,
@@ -75,7 +81,7 @@ export class AuthControllerServer {
75
81
  * This is for login / password authentication invite
76
82
  */
77
83
  static async invite(emailParam) {
78
- const email = emailParam.toLowerCase();
84
+ const email = emailParam?.toLowerCase();
79
85
  const oSafe = getSafeOptions();
80
86
  const existingAccount = await repo(oSafe.Account).findOne({
81
87
  where: {
@@ -88,17 +94,11 @@ export class AuthControllerServer {
88
94
  }
89
95
  else {
90
96
  const token = generateAndEncodeToken();
91
- // TODO: Do we create the user or just the account ?!
92
- // TODO 2: Invite is by mail... But the invitee can log with another provider... So what do we do?! maybe not checking the provider... and updating?
93
- // const user = await repo(oSafe.User).insert({
94
- // identifier: email,
95
- // })
96
97
  await repo(oSafe.Account).insert({
97
98
  provider: FFAuthProvider.PASSWORD.id,
98
99
  providerUserId: email,
99
- // userId: user.id,
100
- // hashPassword: await passwordHash(password),
101
- token: token,
100
+ email,
101
+ token,
102
102
  expiresAt: createDate(AUTH_OPTIONS.providers?.password?.mail?.verify?.expiresIn ?? 5 * 60),
103
103
  lastVerifiedAt: undefined,
104
104
  });
@@ -116,23 +116,19 @@ export class AuthControllerServer {
116
116
  await sendMail('invitationSend', {
117
117
  to: email,
118
118
  subject: 'Invitation',
119
- templateProps: {
120
- title: 'Invitation 👋',
121
- previewText: 'This is the mail you were waiting for',
122
- sections: [
123
- {
124
- text: 'Today is your lucky day !',
125
- highlighted: true,
119
+ title: 'Invitation 👋',
120
+ sections: [
121
+ {
122
+ html: 'Today is your lucky day !',
123
+ },
124
+ {
125
+ html: 'You were invited to join the team',
126
+ cta: {
127
+ html: 'JOIN',
128
+ link: url,
126
129
  },
127
- {
128
- text: 'You were invited to join the team',
129
- cta: {
130
- text: 'JOIN',
131
- link: url,
132
- },
133
- },
134
- ],
135
- },
130
+ },
131
+ ],
136
132
  });
137
133
  authModuleRaw.log.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
138
134
  return {
@@ -158,7 +154,7 @@ export class AuthControllerServer {
158
154
  if (!oSafe.password.enabled) {
159
155
  throw new EntityError({ message: 'Password is not enabled!' });
160
156
  }
161
- const email = emailParam.toLowerCase();
157
+ const email = emailParam?.toLowerCase();
162
158
  oSafe.password.validateInput({ identifier: email, password });
163
159
  const existingAccount = await repo(oSafe.Account).findOne({
164
160
  where: {
@@ -173,11 +169,12 @@ export class AuthControllerServer {
173
169
  // REMULT: Do not put it in a transaction, as it will be called from a backendmethod that is already in a transaction! And nested transactions not allowed.
174
170
  // await remult.dataProvider.transaction(async () => {
175
171
  const user = await repo(oSafe.User).insert({
176
- identifier: email,
172
+ name: nameify(email),
177
173
  });
178
174
  await repo(oSafe.Account).insert({
179
175
  provider: FFAuthProvider.PASSWORD.id,
180
176
  providerUserId: email,
177
+ email,
181
178
  userId: user.id,
182
179
  hashPassword: await oSafe.password.hash(password),
183
180
  token: oSafe.verifiedMethod === 'auto' ? undefined : token,
@@ -188,9 +185,6 @@ export class AuthControllerServer {
188
185
  });
189
186
  // })
190
187
  if (oSafe.verifiedMethod === 'auto') {
191
- const user = await repo(oSafe.User).findFirst({
192
- identifier: email,
193
- });
194
188
  if (user) {
195
189
  const session = await ff_createSession(user.id);
196
190
  return {
@@ -210,19 +204,16 @@ export class AuthControllerServer {
210
204
  await sendMail('verifyMailSend', {
211
205
  to: email,
212
206
  subject: 'Wecome',
213
- templateProps: {
214
- title: 'Wecome 👋',
215
- previewText: 'This is the mail you were waiting for',
216
- sections: [
217
- {
218
- text: 'You can validate your account',
219
- cta: {
220
- text: 'HERE',
221
- link: url,
222
- },
207
+ title: 'Wecome 👋',
208
+ sections: [
209
+ {
210
+ html: 'You can validate your account',
211
+ cta: {
212
+ html: 'HERE',
213
+ link: url,
223
214
  },
224
- ],
225
- },
215
+ },
216
+ ],
226
217
  });
227
218
  authModuleRaw.log.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
228
219
  }
@@ -241,7 +232,7 @@ export class AuthControllerServer {
241
232
  * _(The first param `email` can be "anything")_
242
233
  */
243
234
  static async signInPassword(emailParam, password) {
244
- const email = emailParam.toLowerCase();
235
+ const email = emailParam?.toLowerCase();
245
236
  const oSafe = getSafeOptions();
246
237
  oSafe.password.validateInput({ identifier: email, password });
247
238
  if (!oSafe.password.enabled) {
@@ -258,22 +249,26 @@ export class AuthControllerServer {
258
249
  if (validPassword) {
259
250
  const session = await ff_createSession(existingAccount.userId);
260
251
  const user = await repo(oSafe.User).findId(existingAccount.userId);
252
+ if (!user) {
253
+ authModuleRaw.log.error('User not found for this arround:', existingAccount);
254
+ throw new EntityError({ message: 'User not found, please contact support.' });
255
+ }
261
256
  return {
262
- message: 'ok',
257
+ message: 'Signing in progress...',
263
258
  user: oSafe.transformDbUserToClientUser(session, user),
264
259
  };
265
260
  }
266
- authModuleRaw.log.error({ email, passwordLength: password.length });
261
+ authModuleRaw.log.error({ email, passwordLengthInfo: password.length });
267
262
  throw new EntityError({ message: 'Incorrect username or password' });
268
263
  }
269
- authModuleRaw.log.error({ email, passwordLength: password.length });
264
+ authModuleRaw.log.error({ email, passwordLengthInfo: password.length });
270
265
  throw new EntityError({ message: 'Incorrect username or password.' });
271
266
  }
272
267
  /**
273
268
  * Forgot your password ? Send a mail to reset it.
274
269
  */
275
270
  static async forgotPassword(emailParam) {
276
- const email = emailParam.toLowerCase();
271
+ const email = emailParam?.toLowerCase();
277
272
  const oSafe = getSafeOptions();
278
273
  if (!oSafe.password.enabled) {
279
274
  throw new EntityError({ message: 'Password is not enabled!' });
@@ -303,32 +298,30 @@ export class AuthControllerServer {
303
298
  await sendMail('resetPasswordSend', {
304
299
  to: email,
305
300
  subject: 'Reset your password',
306
- templateProps: {
307
- title: 'Reset your password 👋',
308
- previewText: 'This is the mail you were waiting for',
309
- sections: [
310
- {
311
- text: 'Did you forgot something ?',
312
- highlighted: true,
313
- },
314
- {
315
- text: 'No worries, you can reset your password',
316
- cta: {
317
- text: 'HERE',
318
- link: url,
319
- },
301
+ sections: [
302
+ {
303
+ html: 'Did you forgot something ?',
304
+ },
305
+ {
306
+ html: 'No worries, you can reset your password',
307
+ cta: {
308
+ html: 'HERE',
309
+ link: url,
320
310
  },
321
- ],
322
- },
311
+ },
312
+ ],
323
313
  });
324
314
  authModuleRaw.log.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
325
315
  return {
326
- message: `Demo | ${oSafe.strings.resetPasswordSend}`,
316
+ message: `${oSafe.strings.resetPasswordSend} (DEMO)`,
327
317
  user: remult.user,
328
318
  };
329
319
  }
330
320
  }
331
- throw new EntityError({ message: oSafe.strings.anErrorOccurred });
321
+ return {
322
+ message: oSafe.strings.resetPasswordUnknownUser,
323
+ user: remult.user,
324
+ };
332
325
  }
333
326
  /**
334
327
  * Reset your password with a token
@@ -349,7 +342,7 @@ export class AuthControllerServer {
349
342
  throw new EntityError({ message: 'token expired' });
350
343
  }
351
344
  if (account.userId === undefined) {
352
- const user = await repo(oSafe.User).insert({ identifier: account.providerUserId });
345
+ const user = await repo(oSafe.User).insert({ name: nameify(account.providerUserId) });
353
346
  account.userId = user.id;
354
347
  }
355
348
  await invalidateSession(account.userId);
@@ -367,7 +360,7 @@ export class AuthControllerServer {
367
360
  }
368
361
  /** OTP */
369
362
  static async signInOTP(emailParam) {
370
- const email = emailParam.toLowerCase();
363
+ const email = emailParam?.toLowerCase();
371
364
  const oSafe = getSafeOptions();
372
365
  if (!oSafe.otp.enabled) {
373
366
  throw new EntityError({ message: `OPT is not enabled!` });
@@ -381,20 +374,16 @@ export class AuthControllerServer {
381
374
  const issuer = AUTH_OPTIONS.providers.otp.issuer ?? 'firstly';
382
375
  const uri = createTOTPKeyURI(issuer, email, key, intervalInSeconds, digits);
383
376
  const oSafe = getSafeOptions();
384
- let user = await repo(oSafe.User).findFirst({ identifier: email });
385
- if (!user) {
386
- user = repo(oSafe.User).create();
387
- }
388
- user.identifier = email;
389
- user = await repo(oSafe.User).save(user);
390
- let account = await remult
391
- .repo(oSafe.Account)
392
- .findFirst({ userId: user.id, provider: FFAuthProvider.OTP.id });
393
- if (!account) {
394
- account = repo(oSafe.Account).create();
377
+ const account = await repo(oSafe.Account).upsert({
378
+ where: {
379
+ provider: FFAuthProvider.OTP.id,
380
+ providerUserId: email,
381
+ },
382
+ });
383
+ if (account.userId === '' || account.userId === undefined) {
384
+ const user = await repo(oSafe.User).insert({ name: nameify(email) });
385
+ account.userId = user.id;
395
386
  }
396
- account.userId = user.id;
397
- account.provider = FFAuthProvider.OTP.id;
398
387
  account.token = otp;
399
388
  account.hashPassword = keyEncoded;
400
389
  await repo(oSafe.Account).save(account);
@@ -417,7 +406,7 @@ export class AuthControllerServer {
417
406
  * Verify the OTP code
418
407
  */
419
408
  static async verifyOtp(emailParam, otp) {
420
- const email = emailParam.toLowerCase();
409
+ const email = emailParam?.toLowerCase();
421
410
  const oSafe = getSafeOptions();
422
411
  if (!oSafe.otp.enabled) {
423
412
  throw new EntityError({ message: `OPT is not enabled!` });
@@ -429,8 +418,7 @@ export class AuthControllerServer {
429
418
  throw new EntityError({ message: 'Invalid otp' });
430
419
  }
431
420
  const account = accounts[0];
432
- const user = await repo(oSafe.User).findId(account.userId);
433
- if (user?.identifier !== email) {
421
+ if (account?.providerUserId !== email) {
434
422
  throw new EntityError({ message: 'Invalid otp.' });
435
423
  }
436
424
  const intervalInSeconds = oSafe.providers?.otp?.expiresIn ?? 30;
@@ -447,6 +435,11 @@ export class AuthControllerServer {
447
435
  account.expiresAt = undefined;
448
436
  await repo(oSafe.Account).save(account);
449
437
  const session = await ff_createSession(account.userId);
438
+ const user = await repo(oSafe.User).findId(account.userId);
439
+ if (!user) {
440
+ authModuleRaw.log.error('User not found for this arround:', account);
441
+ throw new EntityError({ message: 'User not found, please contact support.' });
442
+ }
450
443
  return {
451
444
  message: 'verified',
452
445
  user: oSafe.transformDbUserToClientUser(session, user),
@@ -1,2 +1,4 @@
1
- import { type Handle } from '@sveltejs/kit';
2
- export declare const handleAuth: Handle;
1
+ import { redirect as redirectKit, type Handle } from '@sveltejs/kit';
2
+ export declare const handleAuth: ({ redirect }: {
3
+ redirect: typeof redirectKit;
4
+ }) => Handle;
@@ -1,10 +1,10 @@
1
- import { redirect } from '@sveltejs/kit';
1
+ import { redirect as redirectKit } from '@sveltejs/kit';
2
2
  import { repo } from 'remult';
3
3
  import { read } from '@kitql/internals';
4
4
  import { FFAuthProvider } from '../Entities';
5
5
  import { ff_createSession } from './helperFirstly';
6
6
  import { getSafeOptions } from './module';
7
- export const handleAuth = async ({ event, resolve }) => {
7
+ export const handleAuth = ({ redirect }) => async ({ event, resolve }) => {
8
8
  const oSafe = getSafeOptions();
9
9
  if (event.url.pathname === oSafe.firstlyData.props.ui?.paths?.verify_email) {
10
10
  const token = event.url.searchParams.get('token') ?? '';
@@ -71,6 +71,8 @@ export const handleAuth = async ({ event, resolve }) => {
71
71
  const redirectUrl = redirectUrlCookie ?? oSafe.redirectUrl;
72
72
  if (!code || !state || !storedState || state !== storedState || !keyState) {
73
73
  redirect(302, redirectUrl);
74
+ // We have to do this because "lib redirect" is not working, so I pass the handle... :(
75
+ throw 'we cannot arrive here!';
74
76
  }
75
77
  const selectedOAuth = oSafe.providers?.oAuths?.find((c) => c.name === keyState);
76
78
  if (selectedOAuth && code) {
@@ -84,6 +86,7 @@ export const handleAuth = async ({ event, resolve }) => {
84
86
  catch (error) {
85
87
  console.error(error);
86
88
  redirect(302, redirectUrl);
89
+ throw 'we cannot arrive here!';
87
90
  }
88
91
  if (!info.providerUserId) {
89
92
  redirect(302, redirectUrl);
@@ -103,7 +106,7 @@ export const handleAuth = async ({ event, resolve }) => {
103
106
  let nameToUse = '';
104
107
  for (let i = 0; i < info.nameOptions.length; i++) {
105
108
  const existingUser = await repo(oSafe.User).findOne({
106
- where: { identifier: info.nameOptions[i] },
109
+ where: { name: info.nameOptions[i] },
107
110
  });
108
111
  if (existingUser) {
109
112
  // Don't do anything
@@ -117,7 +120,7 @@ export const handleAuth = async ({ event, resolve }) => {
117
120
  nameToUse = `${info.nameOptions[0]}-${info.providerUserId}`;
118
121
  }
119
122
  const user = repo(oSafe.User).create();
120
- user.identifier = nameToUse;
123
+ user.name = nameToUse;
121
124
  await repo(oSafe.User).save(user);
122
125
  account = repo(oSafe.Account).create();
123
126
  account.provider = keyState;
@@ -127,6 +130,7 @@ export const handleAuth = async ({ event, resolve }) => {
127
130
  account.lastVerifiedAt = new Date();
128
131
  account.token = tokens.accessToken();
129
132
  account.metadata = { ...info, tokens_data: tokens.data };
133
+ account.email = info.emailOptions.length > 0 ? info.emailOptions[0] : undefined;
130
134
  await repo(oSafe.Account).save(account);
131
135
  await ff_createSession(account.userId);
132
136
  event.cookies.delete(`${keyState}_oauth_state`, { path: '/' });
@@ -1,6 +1,6 @@
1
- import type { ClassType } from 'remult';
2
- import { Log } from '@kitql/helpers';
3
- import { FFAuthUser } from '../Entities';
1
+ import { type ClassType } from 'remult';
2
+ import type { Log } from '@kitql/helpers';
3
+ import { type FFAuthAccount, type FFAuthUser } from '../Entities';
4
4
  /**
5
5
  * will merge the roles and remove duplicates
6
6
  * will return a new array & a status if the array was changed
@@ -9,4 +9,11 @@ export declare const mergeRoles: (existing: string[], newOnes: string[] | undefi
9
9
  roles: string[];
10
10
  changed: boolean;
11
11
  };
12
- export declare const initRoleFromEnv: (log: Log, userEntity: ClassType<FFAuthUser>, envKey: string, role: string) => Promise<void>;
12
+ export declare const linkRoleToUsersFromEnv: (o: {
13
+ log: Log;
14
+ accountEntity: ClassType<FFAuthAccount>;
15
+ userEntity: ClassType<FFAuthUser>;
16
+ envKey: string;
17
+ envValue: string;
18
+ roles: string[];
19
+ }) => Promise<void>;