firstly 0.0.6 → 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 CHANGED
@@ -1,5 +1,17 @@
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
+
3
15
  ## 0.0.6
4
16
 
5
17
  ### Patch Changes
package/esm/ROUTES.d.ts CHANGED
@@ -32,16 +32,16 @@ declare const AllObjs: {
32
32
  repo?: (string | number);
33
33
  }) => string;
34
34
  "/": string;
35
+ "/auth": string;
35
36
  "/mail": string;
36
- "/remult": string;
37
- "/remult/auth": string;
38
- "/remult/enum": string;
39
- "/remult/select": string;
40
- "/ui": string;
37
+ "/ui/dialog": string;
38
+ "/ui/enum": string;
39
+ "/ui/fieldGroup": string;
40
+ "/ui/select": string;
41
41
  };
42
42
  type AllTypes = typeof AllObjs;
43
43
  export type Routes = keyof AllTypes extends `${string}/${infer Route}` ? `/${Route}` : keyof AllTypes;
44
- export declare const routes: ("/" | "/mail" | "/remult" | "/remult/auth" | "/remult/enum" | "/remult/select" | "/ui" | "firstly_sign_in" | "remult_admin" | "github")[];
44
+ export declare const routes: ("/" | "/auth" | "/mail" | "/ui/dialog" | "/ui/enum" | "/ui/fieldGroup" | "/ui/select" | "firstly_sign_in" | "remult_admin" | "github")[];
45
45
  /**
46
46
  * To be used like this:
47
47
  * ```ts
@@ -70,12 +70,12 @@ export declare function route<T extends NonFunctionKeys<AllTypes>>(key: T): stri
70
70
  export type KIT_ROUTES = {
71
71
  PAGES: {
72
72
  '/': never;
73
+ '/auth': never;
73
74
  '/mail': never;
74
- '/remult': never;
75
- '/remult/auth': never;
76
- '/remult/enum': never;
77
- '/remult/select': never;
78
- '/ui': never;
75
+ '/ui/dialog': never;
76
+ '/ui/enum': never;
77
+ '/ui/fieldGroup': never;
78
+ '/ui/select': never;
79
79
  };
80
80
  SERVERS: Record<string, never>;
81
81
  ACTIONS: Record<string, never>;
package/esm/ROUTES.js CHANGED
@@ -9,12 +9,12 @@
9
9
  */
10
10
  const PAGES = {
11
11
  "/": `/`,
12
+ "/auth": `/auth`,
12
13
  "/mail": `/mail`,
13
- "/remult": `/remult`,
14
- "/remult/auth": `/remult/auth`,
15
- "/remult/enum": `/remult/enum`,
16
- "/remult/select": `/remult/select`,
17
- "/ui": `/ui`
14
+ "/ui/dialog": `/ui/dialog`,
15
+ "/ui/enum": `/ui/enum`,
16
+ "/ui/fieldGroup": `/ui/fieldGroup`,
17
+ "/ui/select": `/ui/select`
18
18
  };
19
19
  /**
20
20
  * SERVERS
@@ -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
- ...user,
14
+ ...userInfo,
14
15
  attributes: {
15
- ...user,
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.name = name;
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,22 +75,34 @@ export class AuthControllerServer {
75
75
  */
76
76
  static async invite(email) {
77
77
  const oSafe = getSafeOptions();
78
- const existingUser = await remult.repo(oSafe.User).findOne({ where: { name: email } });
79
- if (existingUser) {
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 user = await remult.repo(oSafe.User).insert({
84
- name: email,
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(`Done with custom ${green('invitationSend')} (${yellow(url)})`);
101
+ logAuth.success(`${green('[custom]')}${magenta('[invitationSend]')} (${yellow(url)})`);
90
102
  return 'Mail sent !';
91
103
  }
92
104
  else {
93
- await sendMail('invite', {
105
+ await sendMail('invitationSend', {
94
106
  to: email,
95
107
  subject: 'Invitation',
96
108
  templateProps: {
@@ -111,7 +123,7 @@ export class AuthControllerServer {
111
123
  ],
112
124
  },
113
125
  });
114
- logAuth.success(`Done with ${green('sendMail')} (${url})`);
126
+ logAuth.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
115
127
  return 'Demo Mail sent !';
116
128
  }
117
129
  }
@@ -129,37 +141,49 @@ export class AuthControllerServer {
129
141
  if (!oSafe.password_enabled) {
130
142
  throw Error('Password is not enabled!');
131
143
  }
132
- const existingUser = await remult.repo(oSafe.User).findOne({ where: { name: email } });
133
- if (existingUser) {
144
+ const existingAccount = await remult.repo(oSafe.Account).findOne({
145
+ where: {
146
+ providerUserId: email,
147
+ provider: FFAuthProvider.PASSWORD.id,
148
+ },
149
+ });
150
+ if (existingAccount) {
134
151
  throw Error("You can't signup twice !");
135
152
  }
136
153
  checkPassword(password);
137
- const user = await remult.repo(oSafe.User).insert({
138
- name: email,
139
- });
140
154
  const token = generateId(40);
141
- await remult.repo(oSafe.Account).insert({
142
- provider: FFAuthProvider.PASSWORD.id,
143
- providerUserId: email,
144
- userId: user.id,
145
- hashPassword: await passwordHash(password),
146
- token: oSafe.verifiedMethod === 'auto' ? undefined : token,
147
- expiresAt: oSafe.verifiedMethod === 'auto'
148
- ? undefined
149
- : createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60, 's')),
150
- lastVerifiedAt: oSafe.verifiedMethod === 'auto' ? new Date() : undefined,
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
+ });
151
170
  });
152
171
  if (oSafe.verifiedMethod === 'auto') {
153
- await createSession(user.id);
172
+ const user = await remult.repo(oSafe.User).findFirst({
173
+ identifier: email,
174
+ });
175
+ if (user) {
176
+ await createSession(user.id);
177
+ }
154
178
  }
155
179
  else {
156
180
  const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
157
181
  if (AUTH_OPTIONS.providers?.password?.verifyMailSend) {
158
182
  await AUTH_OPTIONS.providers?.password.verifyMailSend({ email, url });
159
- logAuth.success(`Done with custom ${green('verifyMailSend')} (${yellow(url)})`);
183
+ logAuth.success(`${green('[custom]')}${magenta('[verifyMailSend]')} (${yellow(url)})`);
160
184
  }
161
185
  else {
162
- await sendMail('signUpPassword', {
186
+ await sendMail('verifyMailSend', {
163
187
  to: email,
164
188
  subject: 'Wecome',
165
189
  templateProps: {
@@ -176,7 +200,7 @@ export class AuthControllerServer {
176
200
  ],
177
201
  },
178
202
  });
179
- logAuth.success(`Done with ${green('sendMail')} (${url})`);
203
+ logAuth.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
180
204
  }
181
205
  }
182
206
  return 'ok';
@@ -190,14 +214,16 @@ export class AuthControllerServer {
190
214
  if (!oSafe.password_enabled) {
191
215
  throw Error('Password is not enabled!');
192
216
  }
193
- const existingUser = await remult
194
- .repo(oSafe.User)
195
- .findOne({ where: { name: email }, include: { accounts: true } });
196
- const accountPassword = existingUser?.accounts.find((c) => c.provider === FFAuthProvider.PASSWORD.id);
197
- if (accountPassword && existingUser) {
198
- const validPassword = await passwordVerify(accountPassword?.hashPassword ?? '', password ?? '');
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 ?? '');
199
225
  if (validPassword) {
200
- await createSession(existingUser.id);
226
+ await createSession(existingAccount.userId);
201
227
  return 'ok';
202
228
  }
203
229
  throw Error('Incorrect username or password');
@@ -212,29 +238,25 @@ export class AuthControllerServer {
212
238
  if (!oSafe.password_enabled) {
213
239
  throw Error('Password is not enabled!');
214
240
  }
215
- const u = await remult.repo(getSafeOptions().User).findFirst({ name: email });
216
- if (u) {
217
- let authAccount = await remult.repo(oSafe.Account).findFirst({
218
- userId: u.id,
219
- });
220
- if (!authAccount) {
221
- authAccount = remult.repo(oSafe.Account).create();
222
- authAccount.userId = u.id;
223
- authAccount.provider = FFAuthProvider.PASSWORD.id;
224
- authAccount.providerUserId = email;
225
- }
241
+ const existingAccount = await remult.repo(oSafe.Account).findOne({
242
+ where: {
243
+ providerUserId: email,
244
+ provider: FFAuthProvider.PASSWORD.id,
245
+ },
246
+ });
247
+ if (existingAccount) {
226
248
  const token = generateId(40);
227
- authAccount.token = token;
228
- authAccount.expiresAt = createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.resetPasswordExpiresIn ?? 5 * 60, 's'));
229
- await remult.repo(oSafe.Account).save(authAccount);
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);
230
252
  const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
231
253
  if (AUTH_OPTIONS.providers?.password?.resetPasswordSend) {
232
254
  await AUTH_OPTIONS.providers?.password.resetPasswordSend({ email, url });
233
- logAuth.success(`Done with custom ${green('resetPasswordSend')} (${yellow(url)})`);
255
+ logAuth.success(`${green('[custom]')}${magenta('[resetPasswordSend]')} (${yellow(url)})`);
234
256
  return 'Mail sent !';
235
257
  }
236
258
  else {
237
- await sendMail('forgotPassword', {
259
+ await sendMail('resetPasswordSend', {
238
260
  to: email,
239
261
  subject: 'Reset your password',
240
262
  templateProps: {
@@ -255,7 +277,7 @@ export class AuthControllerServer {
255
277
  ],
256
278
  },
257
279
  });
258
- logAuth.success(`Done with ${green('sendMail')} (${url})`);
280
+ logAuth.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
259
281
  return 'Demo Mail sent !';
260
282
  }
261
283
  }
@@ -308,11 +330,11 @@ export class AuthControllerServer {
308
330
  const issuer = AUTH_OPTIONS.providers.otp.issuer ?? 'firstly';
309
331
  const uri = createTOTPKeyURI(issuer, email, secret);
310
332
  const oSafe = getSafeOptions();
311
- let user = await remult.repo(oSafe.User).findFirst({ name: email });
333
+ let user = await remult.repo(oSafe.User).findFirst({ identifier: email });
312
334
  if (!user) {
313
335
  user = remult.repo(oSafe.User).create();
314
336
  }
315
- user.name = email;
337
+ user.identifier = email;
316
338
  user = await remult.repo(oSafe.User).save(user);
317
339
  let account = await remult
318
340
  .repo(oSafe.Account)
@@ -347,7 +369,7 @@ export class AuthControllerServer {
347
369
  }
348
370
  const account = accounts[0];
349
371
  const user = await remult.repo(oSafe.User).findId(account.userId);
350
- if (user?.name !== email) {
372
+ if (user?.identifier !== email) {
351
373
  throw new Error('Invalid otp.');
352
374
  }
353
375
  const { decodeHex } = await import('oslo/encoding');
@@ -7,7 +7,7 @@ export declare class FFAuthUser {
7
7
  id: string;
8
8
  createdAt: Date;
9
9
  updatedAt?: Date;
10
- name: string;
10
+ identifier: string;
11
11
  roles: string[];
12
12
  accounts: FFAuthAccount[];
13
13
  sessions: FFAuthUserSession[];
@@ -14,7 +14,7 @@ let FFAuthUser = class FFAuthUser {
14
14
  id;
15
15
  createdAt;
16
16
  updatedAt;
17
- name;
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.name.length < 2)
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, "name", void 0);
41
+ ], FFAuthUser.prototype, "identifier", void 0);
42
42
  __decorate([
43
43
  Fields.object({
44
44
  valueConverter: {
@@ -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 names = envValue === undefined ? [] : (envValue ?? '').split(',').map((c) => c.trim());
21
- for (let i = 0; i < names.length; i++) {
22
- const name = names[i].trim();
23
- if (name !== '') {
24
- let user = await repo(userEntity).findFirst({ name });
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({ name, roles: [role] });
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 (names.length > 0) {
38
- log.info(`${cyan(role)}: ${names.map((c) => green(c.trim())).join(', ')} added via ${yellow(`.env`)}.`);
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`)}.`);
@@ -96,7 +96,7 @@ __decorate([
96
96
  BackendMethod({ allowed: true })
97
97
  ], Auth, "signInDemo", null);
98
98
  __decorate([
99
- BackendMethod({ allowed: false })
99
+ BackendMethod({ allowed: true })
100
100
  ], Auth, "invite", null);
101
101
  __decorate([
102
102
  BackendMethod({ allowed: true })
package/esm/auth/index.js CHANGED
@@ -223,7 +223,7 @@ export const auth = (o) => {
223
223
  for (let i = 0; i < info.nameOptions.length; i++) {
224
224
  const existingUser = await remult
225
225
  .repo(oSafe.User)
226
- .findOne({ where: { name: info.nameOptions[i] } });
226
+ .findOne({ where: { identifier: info.nameOptions[i] } });
227
227
  if (existingUser) {
228
228
  // Don't do anything
229
229
  }
@@ -236,7 +236,7 @@ export const auth = (o) => {
236
236
  nameToUse = `${info.nameOptions[0]}-${info.providerUserId}`;
237
237
  }
238
238
  const user = remult.repo(oSafe.User).create();
239
- user.name = nameToUse;
239
+ user.identifier = nameToUse;
240
240
  account = remult.repo(oSafe.Account).create();
241
241
  account.provider = keyState;
242
242
  account.providerUserId = info.providerUserId;
package/esm/mail/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { render } from 'svelte-email';
1
+ import { render } from 'svelty-email';
2
2
  import { cyan, green, Log, magenta, red, sleep, white } from '@kitql/helpers';
3
3
  import { DefaultMail } from '../';
4
4
  const log = new Log('firstly | mail');
@@ -1,4 +1,4 @@
1
- <script>import { Button, Container, Head, Heading, Html, Preview, Section, Text } from "svelte-email";
1
+ <script>import { Button, Container, Head, Heading, Html, Preview, Section, Text } from "svelty-email";
2
2
  export let previewText;
3
3
  export let title;
4
4
  export let sections = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firstly",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "description": "Firstly, an opinionated Remult setup!",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "lucia": "^3.2.0",
32
32
  "nodemailer": "^6.9.13",
33
33
  "oslo": "^1.2.0",
34
- "svelte-email": "^0.0.4",
34
+ "svelty-email": "^0.0.11",
35
35
  "tailwind-merge": "^2.3.0",
36
36
  "tailwindcss": "^3.4.3",
37
37
  "vite": "^5.4.1",