firstly 0.0.5 → 0.0.7
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 +20 -0
- package/esm/ROUTES.d.ts +13 -11
- package/esm/ROUTES.js +6 -5
- package/esm/api/index.d.ts +1 -1
- package/esm/auth/Adapter.js +4 -2
- package/esm/auth/AuthController.server.js +129 -66
- package/esm/auth/Entities.d.ts +1 -1
- package/esm/auth/Entities.js +3 -3
- package/esm/auth/RoleHelpers.js +8 -8
- package/esm/auth/client/Auth.js +1 -1
- package/esm/auth/index.d.ts +4 -7
- package/esm/auth/index.js +41 -36
- package/esm/auth/static/assets/Page-BGTO8LC5.css +1 -0
- package/esm/auth/static/assets/Page-DBWJjlEQ.d.ts +4 -0
- package/esm/auth/static/assets/Page-DBWJjlEQ.js +1 -0
- package/esm/auth/static/assets/Page-RIbXHuZG.d.ts +4 -0
- package/esm/auth/static/assets/Page-RIbXHuZG.js +1 -0
- package/esm/auth/static/assets/Page-apb_xgZT.d.ts +6 -0
- package/esm/auth/static/assets/Page-apb_xgZT.js +18 -0
- package/esm/auth/static/assets/{index-R27C_TlP.css → index-CR_3yNaJ.css} +1 -1
- package/esm/auth/static/assets/index-qfq98Nyd.d.ts +63 -0
- package/esm/auth/static/assets/index-qfq98Nyd.js +2 -0
- package/esm/auth/static/index.html +2 -2
- package/esm/auth/types.d.ts +16 -22
- package/esm/cellsBuildor.js +2 -2
- package/esm/index.d.ts +3 -2
- package/esm/index.js +2 -1
- package/esm/mail/index.d.ts +23 -4
- package/esm/mail/index.js +38 -15
- package/esm/mail/templates/DefaultMail.svelte +66 -0
- package/esm/mail/templates/DefaultMail.svelte.d.ts +28 -0
- package/esm/utils/types.d.ts +3 -0
- package/package.json +2 -1
- package/esm/auth/static/assets/Page-BYzkK4q3.d.ts +0 -5
- package/esm/auth/static/assets/Page-BYzkK4q3.js +0 -1
- package/esm/auth/static/assets/Page-ByIhtXVt.d.ts +0 -5
- package/esm/auth/static/assets/Page-ByIhtXVt.js +0 -8190
- package/esm/auth/static/assets/Page-Do7F0Mzd.d.ts +0 -5
- package/esm/auth/static/assets/Page-Do7F0Mzd.js +0 -1
- package/esm/auth/static/assets/Page-gV58jf2r.css +0 -1
- package/esm/auth/static/assets/index-czJ1PA1n.d.ts +0 -53
- package/esm/auth/static/assets/index-czJ1PA1n.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# firstly
|
|
2
2
|
|
|
3
|
+
## 0.0.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#25](https://github.com/jycouet/firstly/pull/25)
|
|
8
|
+
[`54f2f6a`](https://github.com/jycouet/firstly/commit/54f2f6a833c1977c3163e91ce3172fa8edc9da47)
|
|
9
|
+
Thanks [@jycouet](https://github.com/jycouet)! - adding e2e tests for accounts
|
|
10
|
+
|
|
11
|
+
- [#25](https://github.com/jycouet/firstly/pull/25)
|
|
12
|
+
[`943e9d0`](https://github.com/jycouet/firstly/commit/943e9d0b6d5d6a631dc78661d188a76f254d4632)
|
|
13
|
+
Thanks [@jycouet](https://github.com/jycouet)! - rename name to identifier in db
|
|
14
|
+
|
|
15
|
+
## 0.0.6
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#23](https://github.com/jycouet/firstly/pull/23)
|
|
20
|
+
[`c188eb3`](https://github.com/jycouet/firstly/commit/c188eb3d81a9e75b246387512621b5213bbe8dbd)
|
|
21
|
+
Thanks [@jycouet](https://github.com/jycouet)! - fix auth & mail (logs & co)
|
|
22
|
+
|
|
3
23
|
## 0.0.5
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/esm/ROUTES.d.ts
CHANGED
|
@@ -32,15 +32,16 @@ declare const AllObjs: {
|
|
|
32
32
|
repo?: (string | number);
|
|
33
33
|
}) => string;
|
|
34
34
|
"/": string;
|
|
35
|
-
"/
|
|
36
|
-
"/
|
|
37
|
-
"/
|
|
38
|
-
"/
|
|
39
|
-
"/ui": string;
|
|
35
|
+
"/auth": string;
|
|
36
|
+
"/mail": string;
|
|
37
|
+
"/ui/dialog": string;
|
|
38
|
+
"/ui/enum": string;
|
|
39
|
+
"/ui/fieldGroup": string;
|
|
40
|
+
"/ui/select": string;
|
|
40
41
|
};
|
|
41
42
|
type AllTypes = typeof AllObjs;
|
|
42
43
|
export type Routes = keyof AllTypes extends `${string}/${infer Route}` ? `/${Route}` : keyof AllTypes;
|
|
43
|
-
export declare const routes: ("/" | "/
|
|
44
|
+
export declare const routes: ("/" | "/auth" | "/mail" | "/ui/dialog" | "/ui/enum" | "/ui/fieldGroup" | "/ui/select" | "firstly_sign_in" | "remult_admin" | "github")[];
|
|
44
45
|
/**
|
|
45
46
|
* To be used like this:
|
|
46
47
|
* ```ts
|
|
@@ -69,11 +70,12 @@ export declare function route<T extends NonFunctionKeys<AllTypes>>(key: T): stri
|
|
|
69
70
|
export type KIT_ROUTES = {
|
|
70
71
|
PAGES: {
|
|
71
72
|
'/': never;
|
|
72
|
-
'/
|
|
73
|
-
'/
|
|
74
|
-
'/
|
|
75
|
-
'/
|
|
76
|
-
'/ui': never;
|
|
73
|
+
'/auth': never;
|
|
74
|
+
'/mail': never;
|
|
75
|
+
'/ui/dialog': never;
|
|
76
|
+
'/ui/enum': never;
|
|
77
|
+
'/ui/fieldGroup': never;
|
|
78
|
+
'/ui/select': never;
|
|
77
79
|
};
|
|
78
80
|
SERVERS: Record<string, never>;
|
|
79
81
|
ACTIONS: Record<string, never>;
|
package/esm/ROUTES.js
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
const PAGES = {
|
|
11
11
|
"/": `/`,
|
|
12
|
-
"/
|
|
13
|
-
"/
|
|
14
|
-
"/
|
|
15
|
-
"/
|
|
16
|
-
"/ui": `/ui
|
|
12
|
+
"/auth": `/auth`,
|
|
13
|
+
"/mail": `/mail`,
|
|
14
|
+
"/ui/dialog": `/ui/dialog`,
|
|
15
|
+
"/ui/enum": `/ui/enum`,
|
|
16
|
+
"/ui/fieldGroup": `/ui/fieldGroup`,
|
|
17
|
+
"/ui/select": `/ui/select`
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
19
20
|
* SERVERS
|
package/esm/api/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type Module = {
|
|
|
26
26
|
};
|
|
27
27
|
type Options = Omit<RemultServerOptions<RequestEvent<Partial<Record<string, string>>, string | null>> & {
|
|
28
28
|
modules?: Module[] | undefined;
|
|
29
|
-
mail?: MailOptions
|
|
29
|
+
mail?: MailOptions<any>;
|
|
30
30
|
}, 'entities' | 'controllers' | 'initRequest' | 'initApi'>;
|
|
31
31
|
/**
|
|
32
32
|
* it's basically `remultSveltekit` with the `modules` option
|
package/esm/auth/Adapter.js
CHANGED
|
@@ -7,12 +7,14 @@ export class RemultLuciaAdapter {
|
|
|
7
7
|
if (session) {
|
|
8
8
|
const user = await remult.repo(oSafe.User).findId(session.userId);
|
|
9
9
|
if (user) {
|
|
10
|
+
const { identifier, ...userInfo } = user;
|
|
10
11
|
return [
|
|
11
12
|
{ ...session, attributes: {} },
|
|
12
13
|
{
|
|
13
|
-
...
|
|
14
|
+
...userInfo,
|
|
14
15
|
attributes: {
|
|
15
|
-
...
|
|
16
|
+
...userInfo,
|
|
17
|
+
name: identifier,
|
|
16
18
|
session: { id: session.id, expiresAt: session.expiresAt },
|
|
17
19
|
},
|
|
18
20
|
},
|
|
@@ -3,7 +3,7 @@ import { DEV } from 'esm-env';
|
|
|
3
3
|
import { generateId } from 'lucia';
|
|
4
4
|
import { createDate, TimeSpan } from 'oslo';
|
|
5
5
|
import { remult } from 'remult';
|
|
6
|
-
import { green, yellow } from '@kitql/helpers';
|
|
6
|
+
import { green, magenta, yellow } from '@kitql/helpers';
|
|
7
7
|
import { AUTH_OPTIONS, getSafeOptions, logAuth, lucia } from '.';
|
|
8
8
|
import { sendMail } from '../mail';
|
|
9
9
|
import { FFAuthProvider } from './Entities.js';
|
|
@@ -58,11 +58,11 @@ export class AuthControllerServer {
|
|
|
58
58
|
throw new Error(`${name} not found as demo account!`);
|
|
59
59
|
}
|
|
60
60
|
const oSafe = getSafeOptions();
|
|
61
|
-
let user = await remult.repo(oSafe.User).findFirst({ name });
|
|
61
|
+
let user = await remult.repo(oSafe.User).findFirst({ identifier: name });
|
|
62
62
|
if (!user) {
|
|
63
63
|
user = remult.repo(oSafe.User).create();
|
|
64
64
|
}
|
|
65
|
-
user.
|
|
65
|
+
user.identifier = name;
|
|
66
66
|
const r = mergeRoles(user.roles, account.roles);
|
|
67
67
|
user.roles = r.roles;
|
|
68
68
|
await remult.repo(oSafe.User).save(user);
|
|
@@ -75,28 +75,55 @@ export class AuthControllerServer {
|
|
|
75
75
|
*/
|
|
76
76
|
static async invite(email) {
|
|
77
77
|
const oSafe = getSafeOptions();
|
|
78
|
-
const
|
|
79
|
-
|
|
78
|
+
const existingAccount = await remult.repo(oSafe.Account).findOne({
|
|
79
|
+
where: {
|
|
80
|
+
providerUserId: email,
|
|
81
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
if (existingAccount) {
|
|
80
85
|
// throw Error("Already invited !")
|
|
81
86
|
}
|
|
82
87
|
else {
|
|
83
|
-
const
|
|
84
|
-
|
|
88
|
+
const token = generateId(40);
|
|
89
|
+
await remult.repo(oSafe.Account).insert({
|
|
90
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
91
|
+
providerUserId: email,
|
|
92
|
+
// userId: user.id,
|
|
93
|
+
// hashPassword: await passwordHash(password),
|
|
94
|
+
token: oSafe.verifiedMethod === 'auto' ? undefined : token,
|
|
95
|
+
expiresAt: createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60, 's')),
|
|
96
|
+
lastVerifiedAt: undefined,
|
|
85
97
|
});
|
|
86
|
-
const url = `${remult.context.url.origin}`;
|
|
98
|
+
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
87
99
|
if (AUTH_OPTIONS?.invitationSend) {
|
|
88
100
|
await AUTH_OPTIONS?.invitationSend({ email, url });
|
|
89
|
-
logAuth.success(
|
|
101
|
+
logAuth.success(`${green('[custom]')}${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
90
102
|
return 'Mail sent !';
|
|
91
103
|
}
|
|
92
104
|
else {
|
|
93
|
-
await sendMail('
|
|
105
|
+
await sendMail('invitationSend', {
|
|
94
106
|
to: email,
|
|
95
107
|
subject: 'Invitation',
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
templateProps: {
|
|
109
|
+
title: 'Invitation 👋',
|
|
110
|
+
previewText: 'This is the mail you were waiting for',
|
|
111
|
+
sections: [
|
|
112
|
+
{
|
|
113
|
+
text: 'Today is your lucky day !',
|
|
114
|
+
highlighted: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
text: 'You were invited to join the team',
|
|
118
|
+
cta: {
|
|
119
|
+
text: 'JOIN',
|
|
120
|
+
link: url,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
98
125
|
});
|
|
99
|
-
logAuth.success(
|
|
126
|
+
logAuth.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
100
127
|
return 'Demo Mail sent !';
|
|
101
128
|
}
|
|
102
129
|
}
|
|
@@ -114,43 +141,66 @@ export class AuthControllerServer {
|
|
|
114
141
|
if (!oSafe.password_enabled) {
|
|
115
142
|
throw Error('Password is not enabled!');
|
|
116
143
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
144
|
+
const existingAccount = await remult.repo(oSafe.Account).findOne({
|
|
145
|
+
where: {
|
|
146
|
+
providerUserId: email,
|
|
147
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
if (existingAccount) {
|
|
119
151
|
throw Error("You can't signup twice !");
|
|
120
152
|
}
|
|
121
153
|
checkPassword(password);
|
|
122
|
-
const user = await remult.repo(oSafe.User).insert({
|
|
123
|
-
name: email,
|
|
124
|
-
});
|
|
125
154
|
const token = generateId(40);
|
|
126
|
-
await remult.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
:
|
|
135
|
-
|
|
155
|
+
await remult.dataProvider.transaction(async () => {
|
|
156
|
+
const user = await remult.repo(oSafe.User).insert({
|
|
157
|
+
identifier: email,
|
|
158
|
+
});
|
|
159
|
+
await remult.repo(oSafe.Account).insert({
|
|
160
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
161
|
+
providerUserId: email,
|
|
162
|
+
userId: user.id,
|
|
163
|
+
hashPassword: await passwordHash(password),
|
|
164
|
+
token: oSafe.verifiedMethod === 'auto' ? undefined : token,
|
|
165
|
+
expiresAt: oSafe.verifiedMethod === 'auto'
|
|
166
|
+
? undefined
|
|
167
|
+
: createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60, 's')),
|
|
168
|
+
lastVerifiedAt: oSafe.verifiedMethod === 'auto' ? new Date() : undefined,
|
|
169
|
+
});
|
|
136
170
|
});
|
|
137
171
|
if (oSafe.verifiedMethod === 'auto') {
|
|
138
|
-
await
|
|
172
|
+
const user = await remult.repo(oSafe.User).findFirst({
|
|
173
|
+
identifier: email,
|
|
174
|
+
});
|
|
175
|
+
if (user) {
|
|
176
|
+
await createSession(user.id);
|
|
177
|
+
}
|
|
139
178
|
}
|
|
140
179
|
else {
|
|
141
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui
|
|
180
|
+
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
142
181
|
if (AUTH_OPTIONS.providers?.password?.verifyMailSend) {
|
|
143
182
|
await AUTH_OPTIONS.providers?.password.verifyMailSend({ email, url });
|
|
144
|
-
logAuth.success(
|
|
183
|
+
logAuth.success(`${green('[custom]')}${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
145
184
|
}
|
|
146
185
|
else {
|
|
147
|
-
await sendMail('
|
|
186
|
+
await sendMail('verifyMailSend', {
|
|
148
187
|
to: email,
|
|
149
|
-
subject: 'Wecome
|
|
150
|
-
|
|
151
|
-
|
|
188
|
+
subject: 'Wecome',
|
|
189
|
+
templateProps: {
|
|
190
|
+
title: 'Wecome 👋',
|
|
191
|
+
previewText: 'This is the mail you were waiting for',
|
|
192
|
+
sections: [
|
|
193
|
+
{
|
|
194
|
+
text: 'You can validate your account',
|
|
195
|
+
cta: {
|
|
196
|
+
text: 'HERE',
|
|
197
|
+
link: url,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
152
202
|
});
|
|
153
|
-
logAuth.success(
|
|
203
|
+
logAuth.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
154
204
|
}
|
|
155
205
|
}
|
|
156
206
|
return 'ok';
|
|
@@ -164,14 +214,16 @@ export class AuthControllerServer {
|
|
|
164
214
|
if (!oSafe.password_enabled) {
|
|
165
215
|
throw Error('Password is not enabled!');
|
|
166
216
|
}
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
217
|
+
const existingAccount = await remult.repo(oSafe.Account).findOne({
|
|
218
|
+
where: {
|
|
219
|
+
providerUserId: email,
|
|
220
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
if (existingAccount) {
|
|
224
|
+
const validPassword = await passwordVerify(existingAccount?.hashPassword ?? '', password ?? '');
|
|
173
225
|
if (validPassword) {
|
|
174
|
-
await createSession(
|
|
226
|
+
await createSession(existingAccount.userId);
|
|
175
227
|
return 'ok';
|
|
176
228
|
}
|
|
177
229
|
throw Error('Incorrect username or password');
|
|
@@ -186,35 +238,46 @@ export class AuthControllerServer {
|
|
|
186
238
|
if (!oSafe.password_enabled) {
|
|
187
239
|
throw Error('Password is not enabled!');
|
|
188
240
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
authAccount.userId = u.id;
|
|
197
|
-
authAccount.provider = FFAuthProvider.PASSWORD.id;
|
|
198
|
-
authAccount.providerUserId = email;
|
|
199
|
-
}
|
|
241
|
+
const existingAccount = await remult.repo(oSafe.Account).findOne({
|
|
242
|
+
where: {
|
|
243
|
+
providerUserId: email,
|
|
244
|
+
provider: FFAuthProvider.PASSWORD.id,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
if (existingAccount) {
|
|
200
248
|
const token = generateId(40);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
await remult.repo(oSafe.Account).save(
|
|
204
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui
|
|
249
|
+
existingAccount.token = token;
|
|
250
|
+
existingAccount.expiresAt = createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.resetPasswordExpiresIn ?? 5 * 60, 's'));
|
|
251
|
+
await remult.repo(oSafe.Account).save(existingAccount);
|
|
252
|
+
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
205
253
|
if (AUTH_OPTIONS.providers?.password?.resetPasswordSend) {
|
|
206
254
|
await AUTH_OPTIONS.providers?.password.resetPasswordSend({ email, url });
|
|
207
|
-
logAuth.success(
|
|
255
|
+
logAuth.success(`${green('[custom]')}${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
208
256
|
return 'Mail sent !';
|
|
209
257
|
}
|
|
210
258
|
else {
|
|
211
|
-
await sendMail('
|
|
259
|
+
await sendMail('resetPasswordSend', {
|
|
212
260
|
to: email,
|
|
213
261
|
subject: 'Reset your password',
|
|
214
|
-
|
|
215
|
-
|
|
262
|
+
templateProps: {
|
|
263
|
+
title: 'Reset your password 👋',
|
|
264
|
+
previewText: 'This is the mail you were waiting for',
|
|
265
|
+
sections: [
|
|
266
|
+
{
|
|
267
|
+
text: 'Did you forgot something ?',
|
|
268
|
+
highlighted: true,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
text: 'No worries, you can reset your password',
|
|
272
|
+
cta: {
|
|
273
|
+
text: 'HERE',
|
|
274
|
+
link: url,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
},
|
|
216
279
|
});
|
|
217
|
-
logAuth.success(
|
|
280
|
+
logAuth.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
218
281
|
return 'Demo Mail sent !';
|
|
219
282
|
}
|
|
220
283
|
}
|
|
@@ -267,11 +330,11 @@ export class AuthControllerServer {
|
|
|
267
330
|
const issuer = AUTH_OPTIONS.providers.otp.issuer ?? 'firstly';
|
|
268
331
|
const uri = createTOTPKeyURI(issuer, email, secret);
|
|
269
332
|
const oSafe = getSafeOptions();
|
|
270
|
-
let user = await remult.repo(oSafe.User).findFirst({
|
|
333
|
+
let user = await remult.repo(oSafe.User).findFirst({ identifier: email });
|
|
271
334
|
if (!user) {
|
|
272
335
|
user = remult.repo(oSafe.User).create();
|
|
273
336
|
}
|
|
274
|
-
user.
|
|
337
|
+
user.identifier = email;
|
|
275
338
|
user = await remult.repo(oSafe.User).save(user);
|
|
276
339
|
let account = await remult
|
|
277
340
|
.repo(oSafe.Account)
|
|
@@ -306,7 +369,7 @@ export class AuthControllerServer {
|
|
|
306
369
|
}
|
|
307
370
|
const account = accounts[0];
|
|
308
371
|
const user = await remult.repo(oSafe.User).findId(account.userId);
|
|
309
|
-
if (user?.
|
|
372
|
+
if (user?.identifier !== email) {
|
|
310
373
|
throw new Error('Invalid otp.');
|
|
311
374
|
}
|
|
312
375
|
const { decodeHex } = await import('oslo/encoding');
|
package/esm/auth/Entities.d.ts
CHANGED
package/esm/auth/Entities.js
CHANGED
|
@@ -14,7 +14,7 @@ let FFAuthUser = class FFAuthUser {
|
|
|
14
14
|
id;
|
|
15
15
|
createdAt;
|
|
16
16
|
updatedAt;
|
|
17
|
-
|
|
17
|
+
identifier;
|
|
18
18
|
roles = [];
|
|
19
19
|
accounts;
|
|
20
20
|
sessions;
|
|
@@ -33,12 +33,12 @@ __decorate([
|
|
|
33
33
|
validate: [
|
|
34
34
|
Validators.unique(),
|
|
35
35
|
(e) => {
|
|
36
|
-
if (e.
|
|
36
|
+
if (e.identifier.length < 2)
|
|
37
37
|
throw 'Must be at least 2 characters long';
|
|
38
38
|
},
|
|
39
39
|
],
|
|
40
40
|
})
|
|
41
|
-
], FFAuthUser.prototype, "
|
|
41
|
+
], FFAuthUser.prototype, "identifier", void 0);
|
|
42
42
|
__decorate([
|
|
43
43
|
Fields.object({
|
|
44
44
|
valueConverter: {
|
package/esm/auth/RoleHelpers.js
CHANGED
|
@@ -17,13 +17,13 @@ export const mergeRoles = (existing, newOnes) => {
|
|
|
17
17
|
return { roles: Array.from(result), changed };
|
|
18
18
|
};
|
|
19
19
|
export const initRoleFromEnv = async (log, userEntity, envValue, role) => {
|
|
20
|
-
const
|
|
21
|
-
for (let i = 0; i <
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
let user = await repo(userEntity).findFirst({
|
|
20
|
+
const identifiers = envValue === undefined ? [] : (envValue ?? '').split(',').map((c) => c.trim());
|
|
21
|
+
for (let i = 0; i < identifiers.length; i++) {
|
|
22
|
+
const identifier = identifiers[i].trim();
|
|
23
|
+
if (identifier !== '') {
|
|
24
|
+
let user = await repo(userEntity).findFirst({ identifier });
|
|
25
25
|
if (!user) {
|
|
26
|
-
user = repo(userEntity).create({
|
|
26
|
+
user = repo(userEntity).create({ identifier, roles: [role] });
|
|
27
27
|
await repo(userEntity).save(user);
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
@@ -34,8 +34,8 @@ export const initRoleFromEnv = async (log, userEntity, envValue, role) => {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
if (
|
|
38
|
-
log.info(`${cyan(role)}: ${
|
|
37
|
+
if (identifiers.length > 0) {
|
|
38
|
+
log.info(`${cyan(role)}: ${identifiers.map((c) => green(c.trim())).join(', ')} added via ${yellow(`.env`)}.`);
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
log.info(`${cyan(role)}: No users added via ${yellow(`.env`)}.`);
|
package/esm/auth/client/Auth.js
CHANGED
package/esm/auth/index.d.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { Lucia, type SessionCookieOptions } from 'lucia';
|
|
|
3
3
|
import type { ClassType, UserInfo } from 'remult';
|
|
4
4
|
import { Log } from '@kitql/helpers';
|
|
5
5
|
import type { Module } from '../api';
|
|
6
|
-
import type { ResolvedType } from '../utils/types';
|
|
6
|
+
import type { RecursivePartial, ResolvedType } from '../utils/types';
|
|
7
7
|
import { FFAuthAccount, FFAuthProvider, FFAuthUser, FFAuthUserSession } from './Entities';
|
|
8
|
-
import type { firstlyData } from './types';
|
|
8
|
+
import type { firstlyData, firstlyDataAuth } from './types';
|
|
9
9
|
export type { firstlyData };
|
|
10
10
|
export { FFAuthUser, FFAuthAccount, FFAuthProvider, FFAuthUserSession };
|
|
11
11
|
export type AuthorizationURLOptions = Record<string, {
|
|
@@ -35,11 +35,8 @@ type AuthOptions<TUserEntity extends FFAuthUser = FFAuthUser, TSessionEntity ext
|
|
|
35
35
|
Session?: ClassType<TSessionEntity>;
|
|
36
36
|
Account?: ClassType<TAccountEntity>;
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
base?: string;
|
|
41
|
-
};
|
|
42
|
-
} | false;
|
|
38
|
+
debug?: boolean;
|
|
39
|
+
ui?: false | RecursivePartial<firstlyDataAuth['ui']>;
|
|
43
40
|
/** in secondes @default 15 days */
|
|
44
41
|
sessionExpiresIn?: number;
|
|
45
42
|
sessionCookie?: SessionCookieOptions;
|
package/esm/auth/index.js
CHANGED
|
@@ -15,43 +15,47 @@ import { initRoleFromEnv } from './RoleHelpers';
|
|
|
15
15
|
export { FFAuthUser, FFAuthAccount, FFAuthProvider, FFAuthUserSession };
|
|
16
16
|
export const logAuth = new Log('firstly | auth');
|
|
17
17
|
export { FF_Auth_Role } from './Entities';
|
|
18
|
-
export let AUTH_OPTIONS = {};
|
|
18
|
+
export let AUTH_OPTIONS = { ui: {} };
|
|
19
|
+
const buildUrlOrDefault = (base, userSetting, fallback) => {
|
|
20
|
+
if (userSetting) {
|
|
21
|
+
return `${base}/${userSetting}`;
|
|
22
|
+
}
|
|
23
|
+
return `${base}/${fallback}`;
|
|
24
|
+
};
|
|
19
25
|
export const getSafeOptions = () => {
|
|
20
26
|
const signUp = AUTH_OPTIONS.signUp ?? true;
|
|
21
27
|
const base = AUTH_OPTIONS.ui === false ? 'NO_BASE_PATH' : AUTH_OPTIONS.ui?.paths?.base ?? '/ff/auth';
|
|
22
|
-
const oAuths =
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
// const oAuths =
|
|
29
|
+
// AUTH_OPTIONS.providers?.oAuths?.map((o) => {
|
|
30
|
+
// return o.name
|
|
31
|
+
// }) ?? []
|
|
25
32
|
const firstlyData = {
|
|
26
33
|
module: 'auth',
|
|
34
|
+
debug: AUTH_OPTIONS.debug,
|
|
27
35
|
props: {
|
|
28
|
-
ui:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
verify_email: `${base}/verify-email`,
|
|
50
|
-
},
|
|
36
|
+
ui: AUTH_OPTIONS.ui === false
|
|
37
|
+
? undefined
|
|
38
|
+
: {
|
|
39
|
+
paths: {
|
|
40
|
+
base,
|
|
41
|
+
sign_up: buildUrlOrDefault(base, AUTH_OPTIONS.ui?.paths?.sign_up, 'sign-up'),
|
|
42
|
+
sign_in: buildUrlOrDefault(base, AUTH_OPTIONS.ui?.paths?.sign_in, 'sign-in'),
|
|
43
|
+
forgot_password: buildUrlOrDefault(base, AUTH_OPTIONS.ui?.paths?.forgot_password, 'forgot-password'),
|
|
44
|
+
reset_password: buildUrlOrDefault(base, AUTH_OPTIONS.ui?.paths?.reset_password, 'reset-password'),
|
|
45
|
+
verify_email: buildUrlOrDefault(base, AUTH_OPTIONS.ui?.paths?.verify_email, 'verify-email'),
|
|
46
|
+
},
|
|
47
|
+
strings: {
|
|
48
|
+
email: AUTH_OPTIONS.ui?.strings?.email ?? 'Email',
|
|
49
|
+
email_placeholder: AUTH_OPTIONS.ui?.strings?.email_placeholder ?? 'Your email address',
|
|
50
|
+
password: AUTH_OPTIONS.ui?.strings?.password ?? 'Password',
|
|
51
|
+
btn_sign_up: AUTH_OPTIONS.ui?.strings?.btn_sign_up ?? 'Sign up',
|
|
52
|
+
btn_sign_in: AUTH_OPTIONS.ui?.strings?.btn_sign_in ?? 'Sign in',
|
|
53
|
+
forgot_password: AUTH_OPTIONS.ui?.strings?.forgot_password ?? 'Forgot your password?',
|
|
54
|
+
send_password_reset_instructions: AUTH_OPTIONS.ui?.strings?.send_password_reset_instructions ??
|
|
55
|
+
'Send password reset instructions',
|
|
56
|
+
back_to_sign_in: AUTH_OPTIONS.ui?.strings?.back_to_sign_in ?? 'Back to sign in',
|
|
51
57
|
},
|
|
52
|
-
oAuths,
|
|
53
58
|
},
|
|
54
|
-
},
|
|
55
59
|
},
|
|
56
60
|
};
|
|
57
61
|
let redirectUrl = AUTH_OPTIONS.defaultRedirect ?? '/';
|
|
@@ -110,11 +114,11 @@ export const auth = (o) => {
|
|
|
110
114
|
}
|
|
111
115
|
},
|
|
112
116
|
earlyReturn: async ({ event, resolve }) => {
|
|
113
|
-
if (AUTH_OPTIONS.ui === false) {
|
|
114
|
-
|
|
115
|
-
}
|
|
117
|
+
// if (AUTH_OPTIONS.ui === false) {
|
|
118
|
+
// return { early: false }
|
|
119
|
+
// }
|
|
116
120
|
const oSafe = getSafeOptions();
|
|
117
|
-
if (event.url.pathname === oSafe.firstlyData.props.ui
|
|
121
|
+
if (event.url.pathname === oSafe.firstlyData.props.ui?.paths?.verify_email) {
|
|
118
122
|
const token = event.url.searchParams.get('token') ?? '';
|
|
119
123
|
if (!oSafe.password_enabled) {
|
|
120
124
|
throw Error('Password is not enabled!');
|
|
@@ -144,7 +148,8 @@ export const auth = (o) => {
|
|
|
144
148
|
if (installedFirstlyPath) {
|
|
145
149
|
staticPath = `${installedFirstlyPath}/esm/auth/static/`;
|
|
146
150
|
}
|
|
147
|
-
if (
|
|
151
|
+
if (oSafe.firstlyData.props.ui?.paths?.base &&
|
|
152
|
+
event.url.pathname.startsWith(oSafe.firstlyData.props.ui.paths.base)) {
|
|
148
153
|
const content = read(`${staticPath}index.html`);
|
|
149
154
|
return {
|
|
150
155
|
early: true,
|
|
@@ -218,7 +223,7 @@ export const auth = (o) => {
|
|
|
218
223
|
for (let i = 0; i < info.nameOptions.length; i++) {
|
|
219
224
|
const existingUser = await remult
|
|
220
225
|
.repo(oSafe.User)
|
|
221
|
-
.findOne({ where: {
|
|
226
|
+
.findOne({ where: { identifier: info.nameOptions[i] } });
|
|
222
227
|
if (existingUser) {
|
|
223
228
|
// Don't do anything
|
|
224
229
|
}
|
|
@@ -231,7 +236,7 @@ export const auth = (o) => {
|
|
|
231
236
|
nameToUse = `${info.nameOptions[0]}-${info.providerUserId}`;
|
|
232
237
|
}
|
|
233
238
|
const user = remult.repo(oSafe.User).create();
|
|
234
|
-
user.
|
|
239
|
+
user.identifier = nameToUse;
|
|
235
240
|
account = remult.repo(oSafe.Account).create();
|
|
236
241
|
account.provider = keyState;
|
|
237
242
|
account.providerUserId = info.providerUserId;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
form.s-KT008SdmQprk,form.s-N0DaQgJ_T5_C,form.s-85Tmcm64bnMI{display:flex;flex-direction:column}.message.s-85Tmcm64bnMI:empty{display:none}.message.s-85Tmcm64bnMI{background:var(--pico-muted-border-color);padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border-radius:var(--pico-border-radius);margin-bottom:calc(var(--pico-typography-spacing-vertical) * 2)}.message.error.s-85Tmcm64bnMI{background:var(--pico-del-color);color:#4c1513}form.s-FRwn6sVlvC6z{display:flex;flex-direction:column}.message.s-FRwn6sVlvC6z:empty{display:none}.message.s-FRwn6sVlvC6z{background:var(--pico-muted-border-color);padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border-radius:var(--pico-border-radius);margin-bottom:calc(var(--pico-typography-spacing-vertical) * 2)}.message.error.s-FRwn6sVlvC6z{background:var(--pico-del-color);color:#4c1513}.wrapper.s-NO-a_LAEXXH5{min-height:100vh;display:flex;justify-content:center;align-items:center;margin:0 auto}.form.s-NO-a_LAEXXH5{padding:1rem;max-width:360px;width:100%}.form-footer.s-NO-a_LAEXXH5{margin-top:1rem;display:flex;flex-direction:column;justify-content:center;align-items:center}.fallback.s-NO-a_LAEXXH5{display:flex;justify-content:center;align-items:center}
|