firstly 0.0.16-next.2 → 0.1.0-next.3
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/CHANGELOG.md +33 -0
- package/esm/FF_Entity.d.ts +1 -1
- package/esm/FF_Entity.js +9 -9
- package/esm/ROUTES.d.ts +1 -1
- package/esm/ROUTES.js +2 -2
- package/esm/SqlDatabase/FF_LogToConsole.d.ts +14 -0
- package/esm/SqlDatabase/FF_LogToConsole.js +24 -5
- package/esm/auth/Entities.d.ts +2 -1
- package/esm/auth/Entities.js +18 -14
- package/esm/auth/server/AuthController.server.js +86 -93
- package/esm/auth/server/handleAuth.d.ts +4 -2
- package/esm/auth/server/handleAuth.js +8 -4
- package/esm/auth/server/helperRole.d.ts +11 -4
- package/esm/auth/server/helperRole.js +29 -20
- package/esm/auth/server/index.d.ts +1 -0
- package/esm/auth/server/index.js +1 -0
- package/esm/auth/server/module.d.ts +27 -5
- package/esm/auth/server/module.js +24 -10
- package/esm/auth/server/providers/github.js +13 -2
- package/esm/auth/static/assets/Page-BHW08QWz.css +1 -0
- package/esm/auth/static/assets/{Page-BorYIfy9.d.ts → Page-CTZPxniP.d.ts} +2 -2
- package/esm/auth/static/assets/Page-CTZPxniP.js +1 -0
- package/esm/auth/static/assets/{Page-CqsLm8yQ.d.ts → Page-D0d9iZO-.d.ts} +2 -2
- package/esm/auth/static/assets/{Page-CqsLm8yQ.js → Page-D0d9iZO-.js} +1 -1
- package/esm/auth/static/assets/{Page-Cm4MsdIa.d.ts → Page-DXshwJi7.d.ts} +2 -2
- package/esm/auth/static/assets/Page-DXshwJi7.js +20 -0
- package/esm/auth/static/assets/{index-Borxa2ns.d.ts → index-LRDptjak.d.ts} +3 -20
- package/esm/auth/static/assets/{index-Borxa2ns.js → index-LRDptjak.js} +2 -2
- package/esm/auth/static/index.html +1 -1
- package/esm/auth/types.d.ts +1 -0
- package/esm/bin/cmd.js +16 -16
- package/esm/changeLog/changeLogEntities.d.ts +21 -0
- package/esm/changeLog/changeLogEntities.js +57 -0
- package/esm/changeLog/index.d.ts +3 -15
- package/esm/changeLog/index.js +3 -51
- package/esm/changeLog/server/index.d.ts +39 -28
- package/esm/changeLog/server/index.js +40 -29
- package/esm/cron/Cron.d.ts +11 -0
- package/esm/cron/Cron.js +43 -0
- package/esm/cron/Role_Cron.d.ts +3 -0
- package/esm/cron/Role_Cron.js +3 -0
- package/esm/cron/index.d.ts +3 -0
- package/esm/cron/index.js +3 -0
- package/esm/cron/server/index.d.ts +29 -11
- package/esm/cron/server/index.js +29 -13
- package/esm/feedback/FeedbackController.d.ts +3 -1
- package/esm/feedback/FeedbackController.js +23 -1
- package/esm/feedback/server/index.d.ts +2 -2
- package/esm/feedback/server/index.js +2 -2
- package/esm/feedback/types.d.ts +6 -0
- package/esm/feedback/ui/DialogIssue.svelte +8 -1
- package/esm/feedback/ui/DialogIssues.svelte +1 -1
- package/esm/formats/index.d.ts +1 -1
- package/esm/formats/index.js +1 -1
- package/esm/formats/strings.d.ts +2 -0
- package/esm/formats/strings.js +22 -0
- package/esm/index.d.ts +1 -2
- package/esm/index.js +0 -2
- package/esm/mail/Mail.d.ts +13 -0
- package/esm/mail/Mail.js +51 -0
- package/esm/mail/Role_Mail.d.ts +3 -0
- package/esm/mail/Role_Mail.js +3 -0
- package/esm/mail/index.d.ts +7 -2
- package/esm/mail/index.js +7 -2
- package/esm/mail/server/formatMailHelper.d.ts +16 -0
- package/esm/mail/server/formatMailHelper.js +113 -0
- package/esm/mail/server/index.d.ts +32 -19
- package/esm/mail/server/index.js +113 -38
- package/esm/server/index.d.ts +9 -6
- package/esm/server/index.js +28 -50
- package/esm/svelte/FF_Layout.svelte +2 -2
- package/esm/svelte/dialog/DialogManagement.svelte +2 -2
- package/esm/svelte/helpers.d.ts +1 -0
- package/esm/svelte/index.d.ts +1 -0
- package/esm/svelte/index.js +1 -0
- package/esm/svelte/initRemultSvelteReactivity.d.ts +1 -0
- package/esm/svelte/initRemultSvelteReactivity.js +29 -0
- package/esm/sveltekit/server/index.d.ts +2 -2
- package/esm/sveltekit/server/index.js +2 -2
- package/esm/ui/Button.svelte +1 -1
- package/esm/ui/Field.svelte +5 -3
- package/esm/ui/Field.svelte.d.ts +4 -1
- package/esm/ui/FieldGroup.svelte +2 -1
- package/esm/ui/FieldGroup.svelte.d.ts +4 -1
- package/esm/ui/GridPaginate.svelte +2 -2
- package/esm/ui/GridPaginate2.svelte +1 -1
- package/esm/ui/Loading.svelte +1 -1
- package/esm/ui/Tooltip.svelte +1 -1
- package/esm/ui/dialog/DialogForm.svelte +3 -3
- package/esm/ui/dialog/DialogManagement.svelte +2 -2
- package/esm/ui/dialog/DialogPrimitive.svelte +2 -2
- package/esm/ui/dialog/FormEditAction.svelte +2 -2
- package/esm/ui/internals/FieldContainer.svelte +2 -2
- package/esm/ui/internals/Textarea.svelte +2 -2
- package/esm/ui/internals/select/MultiSelectMelt.svelte +6 -6
- package/esm/ui/internals/select/SelectMelt.svelte +28 -14
- package/esm/ui/internals/select/SelectMelt.svelte.d.ts +4 -1
- package/esm/ui/link/LinkPlus.svelte +2 -2
- package/esm/vite/index.js +50 -52
- package/package.json +15 -12
- package/esm/auth/static/assets/Page-BorYIfy9.js +0 -1
- package/esm/auth/static/assets/Page-Cm4MsdIa.js +0 -20
- package/esm/auth/static/assets/Page-JfNiCSIG.css +0 -1
- package/esm/mail/templates/DefaultMail.svelte +0 -86
- package/esm/mail/templates/DefaultMail.svelte.d.ts +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# firstly
|
|
2
2
|
|
|
3
|
+
## 0.1.0-next.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#140](https://github.com/jycouet/firstly/pull/140)
|
|
8
|
+
[`94b2188`](https://github.com/jycouet/firstly/commit/94b2188c78772f94e7835ab933fcebbe2a37703c)
|
|
9
|
+
Thanks [@jycouet](https://github.com/jycouet)! - [BREAKING] - deprecate firstly module in favor of
|
|
10
|
+
remult module
|
|
11
|
+
|
|
12
|
+
```diff
|
|
13
|
+
remultApi({
|
|
14
|
+
-- modules: [], // firstly modules
|
|
15
|
+
++ modulesFF: [], // firstly modules
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
Then, you can use `modules` level of `remult`
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- [#117](https://github.com/jycouet/firstly/pull/117)
|
|
24
|
+
[`407ed4d`](https://github.com/jycouet/firstly/commit/407ed4db8f4b99f234932965b870d51f6a9c07ca)
|
|
25
|
+
Thanks [@jycouet](https://github.com/jycouet)! - need to pass `redirect` to handleAuth manually
|
|
26
|
+
|
|
27
|
+
- [#117](https://github.com/jycouet/firstly/pull/117)
|
|
28
|
+
[`f30c737`](https://github.com/jycouet/firstly/commit/f30c73781d8f50da08fcdc25f1f7611133ea8b0c)
|
|
29
|
+
Thanks [@jycouet](https://github.com/jycouet)! - switch mail engine to sailkit
|
|
30
|
+
|
|
31
|
+
- [#117](https://github.com/jycouet/firstly/pull/117)
|
|
32
|
+
[`5e1d67e`](https://github.com/jycouet/firstly/commit/5e1d67eb8f75127c3d729945e20b22c40184ee20)
|
|
33
|
+
Thanks [@jycouet](https://github.com/jycouet)! - [BREAKING] - Auth Identifier got removed in favor
|
|
34
|
+
of name in User table.
|
|
35
|
+
|
|
3
36
|
## 0.0.16-next.2
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/esm/FF_Entity.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type EntityOptions } from 'remult';
|
|
2
|
-
export declare function FF_Entity<entityType>(key: string, options
|
|
2
|
+
export declare function FF_Entity<entityType>(key: string, options?: EntityOptions<entityType extends new (...args: any) => any ? InstanceType<entityType> : entityType>): (target: any, info?: import("remult/src/remult3/remult3").ClassDecoratorContextStub<entityType extends infer T ? T extends entityType ? T extends new (...args: any) => any ? entityType : never : never : never> | undefined) => any;
|
package/esm/FF_Entity.js
CHANGED
|
@@ -12,31 +12,31 @@ const toAllow = (permission) => {
|
|
|
12
12
|
export function FF_Entity(key, options) {
|
|
13
13
|
return Entity(key, {
|
|
14
14
|
...options,
|
|
15
|
-
allowApiCrud: options
|
|
16
|
-
allowApiDelete: options
|
|
17
|
-
allowApiInsert: options
|
|
18
|
-
allowApiRead: options
|
|
19
|
-
allowApiUpdate: options
|
|
15
|
+
allowApiCrud: options?.allowApiCrud ?? toAllow(options?.permissionApiCrud),
|
|
16
|
+
allowApiDelete: options?.allowApiDelete ?? toAllow(options?.permissionApiDelete),
|
|
17
|
+
allowApiInsert: options?.allowApiInsert ?? toAllow(options?.permissionApiInsert),
|
|
18
|
+
allowApiRead: options?.allowApiRead ?? toAllow(options?.permissionApiRead),
|
|
19
|
+
allowApiUpdate: options?.allowApiUpdate ?? toAllow(options?.permissionApiUpdate),
|
|
20
20
|
// changesLogs
|
|
21
21
|
saved: async (entity, e) => {
|
|
22
22
|
await options?.saved?.(entity, e);
|
|
23
|
-
if (options
|
|
23
|
+
if (options?.changeLog === false) {
|
|
24
24
|
// Don't log changes
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
if (isBackend()) {
|
|
28
|
-
await recordSaved(entity, e, options
|
|
28
|
+
await recordSaved(entity, e, options?.changeLog);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
deleted: async (entity, e) => {
|
|
33
33
|
await options?.deleted?.(entity, e);
|
|
34
|
-
if (options
|
|
34
|
+
if (options?.changeLog === false) {
|
|
35
35
|
// Don't log changes
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
if (isBackend()) {
|
|
39
|
-
await recordDeleted(entity, e, options
|
|
39
|
+
await recordDeleted(entity, e, options?.changeLog);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
},
|
package/esm/ROUTES.d.ts
CHANGED
package/esm/ROUTES.js
CHANGED
|
@@ -37,8 +37,8 @@ const LINKS = {
|
|
|
37
37
|
"remult_admin": `/api/admin`,
|
|
38
38
|
"github": (params) => {
|
|
39
39
|
params = params ?? {};
|
|
40
|
-
params['owner'] = params['owner'] ?? "jycouet";
|
|
41
|
-
params['repo'] = params['repo'] ?? "firstly";
|
|
40
|
+
params['owner'] = params['owner'] ?? "\"jycouet\"";
|
|
41
|
+
params['repo'] = params['repo'] ?? "\"firstly\"";
|
|
42
42
|
return `https://github.com/${params['owner']}/${params['repo']}`;
|
|
43
43
|
}
|
|
44
44
|
};
|
|
@@ -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
|
-
['
|
|
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.
|
|
31
|
-
first = '
|
|
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
|
];
|
package/esm/auth/Entities.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare class FFAuthUser {
|
|
|
10
10
|
id: string;
|
|
11
11
|
createdAt: Date;
|
|
12
12
|
updatedAt?: Date;
|
|
13
|
-
|
|
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;
|
package/esm/auth/Entities.js
CHANGED
|
@@ -15,7 +15,7 @@ let FFAuthUser = class FFAuthUser {
|
|
|
15
15
|
id;
|
|
16
16
|
createdAt;
|
|
17
17
|
updatedAt;
|
|
18
|
-
|
|
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
|
-
|
|
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, "
|
|
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) =>
|
|
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 "
|
|
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
|
|
56
|
-
if (!
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
{
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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: '
|
|
257
|
+
message: 'Signing in progress...',
|
|
263
258
|
user: oSafe.transformDbUserToClientUser(session, user),
|
|
264
259
|
};
|
|
265
260
|
}
|
|
266
|
-
authModuleRaw.log.error({ email,
|
|
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,
|
|
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
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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:
|
|
316
|
+
message: `${oSafe.strings.resetPasswordSend} (DEMO)`,
|
|
327
317
|
user: remult.user,
|
|
328
318
|
};
|
|
329
319
|
}
|
|
330
320
|
}
|
|
331
|
-
|
|
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({
|
|
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
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
.
|
|
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
|
|
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
|
-
|
|
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),
|