nitro-web 0.0.167 → 0.0.169

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/client/app.tsx CHANGED
@@ -19,7 +19,7 @@ type Settings = {
19
19
  // beforeStoreUpdate: (prevStore: Store | null, newData: Store) => Store
20
20
  isStatic?: boolean
21
21
  layouts: React.FC<LayoutProps>[]
22
- middleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
22
+ allMiddleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
23
23
  name: string
24
24
  titleSeparator?: string
25
25
  }
@@ -52,7 +52,7 @@ export async function setupApp(config: Config, storeContainer: StoreContainer, l
52
52
  beforeApp: config.beforeApp || beforeApp,
53
53
  isStatic: config.isStatic,
54
54
  layouts: layouts,
55
- middleware: Object.assign(defaultMiddleware, config.middleware || {}),
55
+ allMiddleware: Object.assign(middleware, config.middleware || {}),
56
56
  name: config.name,
57
57
  titleSeparator: config.titleSeparator,
58
58
  }
@@ -165,9 +165,9 @@ function getRouter({ settings, config }: { settings: Settings, config: Config })
165
165
  const layoutNum = (parseInt(String(route.meta?.layout || '1')) || 1) - 1
166
166
 
167
167
  // get the routes middleware
168
- const middleware = toArray(route[routePath]).filter((policyNameOrTrue) => {
168
+ const routeMiddleware = toArray(route[routePath]).filter((policyNameOrTrue) => {
169
169
  if (policyNameOrTrue === true) return // ignore true
170
- else if (policyNameOrTrue in settings.middleware) return true
170
+ else if (policyNameOrTrue in settings.allMiddleware) return true
171
171
  else console.error(`No middleware named '${policyNameOrTrue}' defined under config.middleware, skipping..`)
172
172
  })
173
173
 
@@ -180,7 +180,7 @@ function getRouter({ settings, config }: { settings: Settings, config: Config })
180
180
  layout: layoutNum,
181
181
  title: `${route.meta?.title ? `${route.meta.title}${settings.titleSeparator || ' - '}` : ''}${settings.name}`,
182
182
  },
183
- middleware: middleware as string[],
183
+ middleware: routeMiddleware as string[],
184
184
  name: componentName,
185
185
  path: routePath,
186
186
  redirect: route.redirect,
@@ -218,7 +218,7 @@ function getRouter({ settings, config }: { settings: Settings, config: Config })
218
218
  await setTimeoutPromise(() => {}, 0)
219
219
  }
220
220
  for (const key of route.middleware) {
221
- const error = settings.middleware[key](route, exposedStoreData || {})
221
+ const error = settings.allMiddleware[key](route, exposedStoreData || {})
222
222
  // Note: the redirect uses the new pathname for query string values, e.g. '?example=value'. We also can't use the
223
223
  // current pathname, as this doesn't exist on page refresh.
224
224
  if (error && error.redirect) {
@@ -317,22 +317,19 @@ async function beforeApp(config: Config) {
317
317
  return { ...storeData, apiAvailable }
318
318
  }
319
319
 
320
- const defaultMiddleware = {
321
- // Global middleware that can referenced from component routes
320
+ export const middleware = {
321
+ // Default middleware that can referenced from component routes
322
322
  isAdmin: (route: unknown, store: { user?: { type?: string, isAdmin?: boolean } }) => {
323
- const user = store.user || { type: 'visitor' }
324
- if (user?.type?.match(/admin/) || user?.isAdmin) return
325
- else if (user?.type && user?.type !== 'visitor') return { redirect: '/signin?unauth' }
323
+ if (store.user?.type?.match(/admin/) || store.user?.isAdmin) return
324
+ else if (store.user) return { redirect: '/signin?unauth' }
326
325
  else return { redirect: '/signin?signin' }
327
326
  },
328
327
  isSubscribed: (route: unknown, store: { user?: { company?: { currentSubscription?: string } } }) => {
329
- const user = store?.user || { type: 'visitor', company: { currentSubscription: '' } }
330
- if (!user?.company?.currentSubscription) return
328
+ if (store.user?.company?.currentSubscription) return
331
329
  else return { redirect: '/plans/subscribe' }
332
330
  },
333
331
  isUser: (route: unknown, store: { user?: { type?: string } }) => {
334
- const user = store.user || { type: 'visitor' }
335
- if (user?.type !== 'visitor') return
332
+ if (store.user) return
336
333
  else return { redirect: '/signin?signin' }
337
334
  },
338
335
  }
package/client/index.ts CHANGED
@@ -9,7 +9,7 @@ export * as util from 'nitro-web/util'
9
9
  export * from '../types'
10
10
 
11
11
  // Main app functions
12
- export { setupApp, updateJwt } from './app'
12
+ export { setupApp, updateJwt, middleware } from './app'
13
13
  export { createStore, exposedStoreData, preloadedStoreData, setStoreWrapper } from './store'
14
14
 
15
15
  // Component Pages
@@ -19,11 +19,17 @@ export const routes = {
19
19
  'post /api/signin': [signin],
20
20
  'post /api/signup': [signup],
21
21
  'post /api/reset-instructions': [resetInstructions],
22
- 'post /api/reset-password': [resetPassword],
22
+ 'post /api/reset-password': [resetConfirm],
23
23
  'post /api/invite-instructions': [inviteInstructions],
24
- 'post /api/invite-accept': [resetPassword],
24
+ 'post /api/invite-accept': [inviteConfirm],
25
25
  'delete /api/account/:uid': [remove],
26
26
 
27
+ // todo:
28
+ // We dont need all of these overridable, just signinAndGetStore, findUserFromProvider,
29
+ // and getStore. So we will allow just these two to be passed around.
30
+ // userCreate not needed, they can just create their own signup function.
31
+ /// Maybe we can pass these into setup?
32
+
27
33
  // Overridable helpers
28
34
  setup: setup,
29
35
  findUserFromProvider: findUserFromProvider,
@@ -33,14 +39,16 @@ export const routes = {
33
39
  tokenParse: tokenParse,
34
40
  userCreate: userCreate,
35
41
  validatePassword: validatePassword,
42
+ sendToken: sendToken,
43
+ inviteOrResetConfirm: inviteOrResetConfirm,
36
44
  }
37
45
 
38
46
  function setup(middleware, _config) {
39
47
  // routes.setup is called automatically when express starts
40
48
  // Set config values
41
- const configKeys = ['clientUrl', 'emailFrom', 'env', 'name', 'mailgunDomain', 'mailgunKey', 'masterPassword', 'isNotMultiTenant']
49
+ const configKeys = ['baseUrl', 'emailFrom', 'env', 'name', 'mailgunDomain', 'mailgunKey', 'masterPassword', 'isNotMultiTenant']
42
50
  authConfig = pick(_config, configKeys)
43
- for (const key of ['clientUrl', 'emailFrom', 'env', 'name']) {
51
+ for (const key of ['baseUrl', 'emailFrom', 'env', 'name']) {
44
52
  if (!authConfig[key]) throw new Error(`Missing config value for: config.${key}`)
45
53
  }
46
54
 
@@ -140,95 +148,6 @@ function signout(req, res) {
140
148
  res.json('{}')
141
149
  }
142
150
 
143
- async function resetInstructions(req, res) {
144
- try {
145
- let email = (req.body.email || '').trim().toLowerCase()
146
- if (!email) throw { title: 'email', detail: 'The email you entered is incorrect.' }
147
-
148
- let user = await db.user.findOne({ query: { email }, _privateData: true })
149
- if (!user) throw { title: 'email', detail: 'The email you entered is incorrect.' }
150
-
151
- let resetToken = await tokenCreate(user._id)
152
- await db.user.update({ query: { email }, $set: { resetToken }})
153
-
154
- res.json({})
155
- sendEmail({
156
- config: authConfig,
157
- template: 'reset-password',
158
- to: `${ucFirst(user.firstName)}<${email}>`,
159
- data: {
160
- token: resetToken + (req.query.hasOwnProperty('desktop') ? '?desktop' : ''),
161
- },
162
- }).catch(err => console.error('sendEmail(..) mailgun error', err))
163
- } catch (err) {
164
- res.error(err)
165
- }
166
- }
167
-
168
- async function resetPassword(req, res) {
169
- try {
170
- const { token, password, password2 } = req.body
171
- const name = req.path.includes('invite') ? 'inviteToken' : 'resetToken'
172
- const desktop = req.query.desktop
173
- const id = tokenParse(token)
174
- await validatePassword(password, password2)
175
-
176
- let user = await db.user.findOne({ query: id, blacklist: ['-' + name], _privateData: true })
177
- if (!user || user[name] !== token) throw new Error('Sorry your token is invalid or has already been used.')
178
-
179
- await db.user.update({
180
- query: user._id,
181
- data: {
182
- password: await bcrypt.hash(password, 10),
183
- resetToken: '',
184
- },
185
- blacklist: ['-' + name, '-password'],
186
- })
187
- res.send(await this.signinAndGetStore({ ...user, [name]: undefined }, desktop, this.getStore))
188
- } catch (err) {
189
- res.error(err)
190
- }
191
- }
192
-
193
- async function inviteInstructions(req, res) {
194
- try {
195
- // Check if user is admin here rather than in middleware (which may not exist yet)
196
- if (req.user.type != 'admin' && !req.user.isAdmin) {
197
- throw new Error('You are not authorized to invite users.')
198
- }
199
- const inviteToken = await tokenCreate()
200
- const userData = await db.user.validate({
201
- ...pick(req.body, ['email', 'firstName', 'lastName']),
202
- status: 'invited',
203
- inviteToken: inviteToken,
204
- })
205
-
206
- // Check if user already exists
207
- if (await db.user.findOne({ query: { email: userData.email } })) {
208
- throw { title: 'email', detail: 'User already exists.' }
209
- }
210
-
211
- // Create user
212
- const user = await db.user.insert({
213
- data: userData,
214
- })
215
-
216
- // Send email
217
- res.send(user)
218
- sendEmail({
219
- config: authConfig,
220
- template: 'invite-user',
221
- to: `${ucFirst(userData.firstName)}<${userData.email}>`,
222
- data: {
223
- token: inviteToken + (req.query.hasOwnProperty('desktop') ? '?desktop' : ''),
224
- },
225
- }).catch(err => console.error('sendEmail(..) mailgun error', err))
226
-
227
- } catch (err) {
228
- return res.error(err)
229
- }
230
- }
231
-
232
151
  async function remove(req, res) {
233
152
  try {
234
153
  const uid = db.id(req.params.uid || 'badid')
@@ -409,4 +328,124 @@ export async function validatePassword(password='', password2) {
409
328
  } else if (typeof password2 != 'undefined' && password !== password2) {
410
329
  throw [{ title: 'password2', detail: 'Your passwords need to match.' }]
411
330
  }
331
+ }
332
+
333
+
334
+
335
+
336
+ /* ---- Controllers -------------------------- */
337
+
338
+ export async function resetInstructions(req, res) {
339
+ try {
340
+ // const desktop = req.query.hasOwnProperty('desktop') ? '?desktop' : '' // see sendToken for future usage
341
+ let email = (req.body.email || '').trim().toLowerCase()
342
+ if (!email) throw { title: 'email', detail: 'The email you entered is incorrect.' }
343
+
344
+ let user = await db.user.findOne({ query: { email }, _privateData: true })
345
+ if (!user) throw { title: 'email', detail: 'The email you entered is incorrect.' }
346
+
347
+ // Send reset password email
348
+ await sendToken({ type: 'reset', user: user })
349
+ res.json({})
350
+ } catch (err) {
351
+ res.error(err)
352
+ }
353
+ }
354
+
355
+ export async function inviteInstructions(req, res) {
356
+ try {
357
+ // const desktop = req.query.hasOwnProperty('desktop') ? '?desktop' : '' // see sendToken for future usage
358
+ let user = await db.user.findOne({ query: { _id: req.params._id }, _privateData: true })
359
+ if (!user) throw new Error('Invalid user id')
360
+ // Send invite instructions email
361
+ await sendToken({ type: 'invite', user: user })
362
+ res.json({})
363
+ } catch (err) {
364
+ res.error(err)
365
+ }
366
+ }
367
+
368
+ export async function inviteConfirm(req, res) {
369
+ try {
370
+ res.send(await this.inviteOrResetConfirm('invite', req))
371
+ } catch (err) {
372
+ res.error(err)
373
+ }
374
+ }
375
+
376
+ export async function resetConfirm(req, res) {
377
+ try {
378
+ res.send(await this.inviteOrResetConfirm('reset', req))
379
+ } catch (err) {
380
+ res.error(err)
381
+ }
382
+ }
383
+
384
+ /* ---- Helpers ------------------------------ */
385
+
386
+ export async function inviteOrResetConfirm(type, req) {
387
+ const { token, password, password2 } = req.body
388
+ const name = type === 'invite' ? 'inviteToken' : 'resetToken'
389
+ const desktop = req.query.desktop
390
+ const id = tokenParse(token)
391
+ await validatePassword(password, password2)
392
+
393
+ let user = await db.user.findOne({ query: id, blacklist: ['-' + name], _privateData: true })
394
+ if (!user || user[name] !== token) throw new Error('Sorry your token is invalid or has already been used.')
395
+
396
+ await db.user.update({
397
+ query: user._id,
398
+ data: {
399
+ password: await bcrypt.hash(password, 10),
400
+ [name]: '', // remove token
401
+ },
402
+ blacklist: ['-' + name, '-password'],
403
+ })
404
+ const store = await this.signinAndGetStore({ ...user, [name]: undefined }, desktop, this.getStore)
405
+ return store
406
+ }
407
+
408
+ /**
409
+ * Checks if the user exists, updates the user with the invite token and sends the invite email
410
+ * @param {object} options
411
+ * @param {'reset' | 'invite'} options.type - The type of token to send (default: 'reset')
412
+ * @param {{_id: string, email: string, firstName: string}} options.user - The user to send the invite email to
413
+ * @param {function} [options.beforeUpdate] - callback hook to run before updating the user
414
+ * @param {function} [options.beforeSendEmail] - callback hook to run before sending the email
415
+ * @returns {Promise<{token: string, mailgunPromise: Promise<unknown>}>}
416
+ */
417
+ export async function sendToken({ type = 'reset', user, beforeUpdate, beforeSendEmail }) {
418
+ if (!user?._id) throw new Error('user is required')
419
+ if (!user?.email) throw new Error('user.email is required')
420
+ if (!user?.firstName) throw new Error('user.firstName is required')
421
+ const token = await tokenCreate(user._id)
422
+
423
+ // get the data
424
+ const data = beforeUpdate ? beforeUpdate({ [type + 'Token']: token }) : { [type + 'Token']: token }
425
+ if (type === 'invite') data.isInvited = true
426
+
427
+ // Update the user with the token
428
+ const result = await db.user.update({
429
+ query: { _id: user._id },
430
+ data: data,
431
+ blacklist: ['-' + type + 'Token'],
432
+ })
433
+
434
+ if (!result._output.matchedCount) {
435
+ throw new Error('Invalid user id to send the token to')
436
+ }
437
+
438
+ // Send email
439
+ const options = {
440
+ config: authConfig,
441
+ template: type === 'reset' ? 'reset-password' : 'invite-user',
442
+ to: `${ucFirst(user.firstName)}<${user.email}>`,
443
+ data: { token: token }, // + (req.query.hasOwnProperty('desktop') ? '?desktop' : '')
444
+ }
445
+ const mailgunPromise = sendEmail(beforeSendEmail ? beforeSendEmail(options, token) : options).catch(err => {
446
+ console.error('sendEmail(..) mailgun error', err)
447
+ })
448
+
449
+ // Return the token and mailgun promise
450
+ return { token, mailgunPromise }
412
451
  }
@@ -69,7 +69,7 @@ export function ResetPassword({ className, elements, redirectTo }: resetInstruct
69
69
  try {
70
70
  const data = await request('post /api/reset-password', state, event, isLoading, setState)
71
71
  setStore((s) => ({ ...s, ...data }))
72
- navigate(redirectTo || '/')
72
+ setTimeout(() => navigate(redirectTo || '/'), 10) // wait for setStore
73
73
  } catch (e) {
74
74
  return setState({ ...state, errors: e as Errors })
75
75
  }
@@ -51,7 +51,7 @@ export function Signin({ className, elements, redirectTo }: signinProps) {
51
51
  setTimeout(() => { // wait for setStore
52
52
  if (location.search.includes('redirect')) navigate(location.search.replace('?redirect=', ''))
53
53
  else navigate(redirectTo || '/')
54
- }, 100)
54
+ }, 10)
55
55
  } catch (e) {
56
56
  return setState({ ...state, errors: e as Errors})
57
57
  }
@@ -28,7 +28,7 @@ export function Signup({ className, elements, redirectTo }: signupProps) {
28
28
  try {
29
29
  const data = await request('post /api/signup', state, e, isLoading, setState)
30
30
  setStore((prev) => ({ ...prev, ...data }))
31
- setTimeout(() => navigate(redirectTo || '/'), 0) // wait for setStore
31
+ setTimeout(() => navigate(redirectTo || '/'), 10) // wait for setStore
32
32
  } catch (e) {
33
33
  setState((prev) => ({ ...prev, errors: e as Errors }))
34
34
  }
@@ -21,7 +21,7 @@ function setup(middleware, _config) {
21
21
  // Set config values
22
22
  config = {
23
23
  env: _config.env,
24
- clientUrl: _config.clientUrl,
24
+ baseUrl: _config.baseUrl,
25
25
  stripeSecretKey: _config.stripeSecretKey,
26
26
  stripeWebhookSecret: _config.stripeWebhookSecret,
27
27
  }
@@ -81,7 +81,7 @@ async function billingPortalSessionCreate(req, res) {
81
81
  }
82
82
  const session = await stripe.billingPortal.sessions.create({
83
83
  customer: req.user.stripeCustomer.id,
84
- return_url: config.clientUrl + '/subscriptions',
84
+ return_url: config.baseUrl + '/subscriptions',
85
85
  })
86
86
  res.json(session.url)
87
87
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.167",
3
+ "version": "0.0.169",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
6
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
@@ -15,7 +15,7 @@ const _dirname = dirname(fileURLToPath(import.meta.url)) + '/'
15
15
 
16
16
  /**
17
17
  * Sends an email using a predefined template, with optional data/or recipientVariables
18
- * @typedef {{ clientUrl?: string, emailFrom?: string, mailgunDomain?: string, mailgunKey?: string, name?: string }} Config
18
+ * @typedef {{ baseUrl?: string, emailFrom?: string, mailgunDomain?: string, mailgunKey?: string, name?: string }} Config
19
19
  *
20
20
  * @param {object} opts
21
21
  * @param {string} opts.template - Template name or raw HTML, e.g., 'reset-password'
@@ -46,8 +46,8 @@ export async function sendEmail({
46
46
  }) {
47
47
  if (!config) {
48
48
  throw new Error('sendEmail: `config` missing')
49
- } else if (!config.clientUrl) {
50
- throw new Error('sendEmail: `config.clientUrl` is missing')
49
+ } else if (!config.baseUrl) {
50
+ throw new Error('sendEmail: `config.baseUrl` is missing')
51
51
  } else if (!config.emailFrom) {
52
52
  throw new Error('sendEmail: `config.emailFrom` is missing')
53
53
  } else if (!test && (!config.mailgunKey || !config.mailgunDomain)) {
@@ -81,7 +81,7 @@ export async function sendEmail({
81
81
  recipientVariables[toEmail] = {
82
82
  ...(data || {}),
83
83
  configName: ucFirst(config.name),
84
- domain: config.clientUrl,
84
+ domain: config.baseUrl,
85
85
  replyToEmail: getNameEmail(replyTo)[1],
86
86
  replyToName: getNameEmail(replyTo)[0],
87
87
  email: toEmail,
@@ -95,7 +95,7 @@ export async function sendEmail({
95
95
  bcc: bcc,
96
96
  emailTemplateDir: getDirectories(path, config.pwd).emailTemplateDir,
97
97
  from: from,
98
- isDev: config.clientUrl.match(/:/), // possibly use config.env here
98
+ isDev: config.baseUrl.match(/:/), // possibly use config.env here
99
99
  recipientVariables: recipientVariables,
100
100
  replyTo: replyTo,
101
101
  skipCssInline: skipCssInline,
@@ -103,7 +103,7 @@ export async function sendEmail({
103
103
  template: template,
104
104
  test: config.emailTestMode || test,
105
105
  to: to,
106
- url: config.clientUrl,
106
+ url: config.baseUrl,
107
107
  }
108
108
 
109
109
  // Grab html and send
@@ -1,3 +1,11 @@
1
+
2
+ <!--
3
+ todo: change naming to:
4
+ invite-link
5
+ invite-confirm
6
+ reset-link
7
+ reset-confirm
8
+ -->
1
9
  <!-- extends references the default nitro layout file -->
2
10
  {% extends "partials/layout1.swig" %}
3
11
 
@@ -7,20 +7,23 @@ export default {
7
7
  avatar: { type: 'image' },
8
8
  company: { model: 'company', required: true },
9
9
  email: { type: 'email', required: true, index: 'unique' },
10
+ isInvited: { type: 'boolean' },
10
11
  firstName: { type: 'string', required: true },
11
12
  lastName: { type: 'string', required: true },
12
- password: { type: 'string', minLength: 6 },
13
- resetToken: { type: 'string' },
14
13
  status: { type: 'string', default: 'active', enum: ['active', 'deleted', 'inactive'] },
15
14
  stripeCustomer: { type: 'any' },
16
15
  stripeSubscription: { type: 'any' },
17
16
  stripeIntents: { type: 'any' },
18
17
  type: { type: 'string', default: 'user', enum: ['user', 'admin'] },
19
18
  usedFreeTrial: { type: 'boolean', default: false },
19
+ // hidden fields
20
+ password: { type: 'string', minLength: 6 },
21
+ inviteToken: { type: 'string' },
22
+ resetToken: { type: 'string' },
20
23
  },
21
24
 
22
- findBL: ['password', 'resetToken'],
23
- updateBL: ['company', 'password', 'resetToken', 'status', 'stripeSubscription', 'type', 'usedFreeTrial'],
25
+ findBL: ['password', 'inviteToken', 'resetToken'],
26
+ updateBL: ['password', 'inviteToken', 'resetToken', 'company', 'status', 'stripeSubscription', 'type', 'usedFreeTrial'],
24
27
 
25
28
  messages: {
26
29
  lastName: {
package/server/router.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // @ts-nocheck
2
+ // renamed to app, similar to client/app.tsx
2
3
  import fs from 'fs'
3
4
  import path, { dirname } from 'path'
4
5
  import http from 'http'
@@ -11,15 +11,42 @@ export function userCreate({ business, password, ...userDataProp }: {
11
11
  export function tokenCreate(id: any): Promise<any>;
12
12
  export function tokenParse(token: any): any;
13
13
  export function validatePassword(password: string, password2: any): Promise<void>;
14
+ export function resetInstructions(req: any, res: any): Promise<void>;
15
+ export function inviteInstructions(req: any, res: any): Promise<void>;
16
+ export function inviteConfirm(req: any, res: any): Promise<void>;
17
+ export function resetConfirm(req: any, res: any): Promise<void>;
18
+ export function inviteOrResetConfirm(type: any, req: any): Promise<any>;
19
+ /**
20
+ * Checks if the user exists, updates the user with the invite token and sends the invite email
21
+ * @param {object} options
22
+ * @param {'reset' | 'invite'} options.type - The type of token to send (default: 'reset')
23
+ * @param {{_id: string, email: string, firstName: string}} options.user - The user to send the invite email to
24
+ * @param {function} [options.beforeUpdate] - callback hook to run before updating the user
25
+ * @param {function} [options.beforeSendEmail] - callback hook to run before sending the email
26
+ * @returns {Promise<{token: string, mailgunPromise: Promise<unknown>}>}
27
+ */
28
+ export function sendToken({ type, user, beforeUpdate, beforeSendEmail }: {
29
+ type: "reset" | "invite";
30
+ user: {
31
+ _id: string;
32
+ email: string;
33
+ firstName: string;
34
+ };
35
+ beforeUpdate?: Function;
36
+ beforeSendEmail?: Function;
37
+ }): Promise<{
38
+ token: string;
39
+ mailgunPromise: Promise<unknown>;
40
+ }>;
14
41
  export const routes: {
15
42
  'get /api/store': (typeof store)[];
16
43
  'get /api/signout': (typeof signout)[];
17
44
  'post /api/signin': (typeof signin)[];
18
45
  'post /api/signup': (typeof signup)[];
19
46
  'post /api/reset-instructions': (typeof resetInstructions)[];
20
- 'post /api/reset-password': (typeof resetPassword)[];
47
+ 'post /api/reset-password': (typeof resetConfirm)[];
21
48
  'post /api/invite-instructions': (typeof inviteInstructions)[];
22
- 'post /api/invite-accept': (typeof resetPassword)[];
49
+ 'post /api/invite-accept': (typeof inviteConfirm)[];
23
50
  'delete /api/account/:uid': (typeof remove)[];
24
51
  setup: typeof setup;
25
52
  findUserFromProvider: typeof findUserFromProvider;
@@ -29,14 +56,13 @@ export const routes: {
29
56
  tokenParse: typeof tokenParse;
30
57
  userCreate: typeof userCreate;
31
58
  validatePassword: typeof validatePassword;
59
+ sendToken: typeof sendToken;
60
+ inviteOrResetConfirm: typeof inviteOrResetConfirm;
32
61
  };
33
62
  declare function store(req: any, res: any): Promise<void>;
34
63
  declare function signout(req: any, res: any): void;
35
64
  declare function signin(req: any, res: any): any;
36
65
  declare function signup(req: any, res: any): Promise<void>;
37
- declare function resetInstructions(req: any, res: any): Promise<void>;
38
- declare function resetPassword(req: any, res: any): Promise<void>;
39
- declare function inviteInstructions(req: any, res: any): Promise<any>;
40
66
  declare function remove(req: any, res: any): Promise<void>;
41
67
  declare function setup(middleware: any, _config: any): void;
42
68
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"auth.api.d.ts","sourceRoot":"","sources":["../../../components/auth/auth.api.js"],"names":[],"mappings":"AAmQA,qGAyCC;AAED;;GAKC;AAED,0FAQC;AAED;;;;iBAkDC;AAED,mDAOC;AAED,4CAYC;AAED,kFAiBC;AA7YD;;;;;;;;;;;;;;;;;;EAqBC;AAmED,0DAEC;AAkCD,mDAEC;AAnBD,iDAeC;AA9BD,2DAaC;AAuBD,sEAuBC;AAED,kEAuBC;AAED,sEAqCC;AAED,2DAwBC;AA1ND,4DA+DC"}
1
+ {"version":3,"file":"auth.api.d.ts","sourceRoot":"","sources":["../../../components/auth/auth.api.js"],"names":[],"mappings":"AAkLA,qGAyCC;AAED;;GAKC;AAED,0FAQC;AAED;;;;iBAkDC;AAED,mDAOC;AAED,4CAYC;AAED,kFAiBC;AAOD,qEAeC;AAED,sEAWC;AAED,iEAMC;AAED,gEAMC;AAID,wEAoBC;AAED;;;;;;;;GAQG;AACH,yEANG;IAAoC,IAAI,EAAhC,OAAO,GAAG,QAAQ;IACuC,IAAI,EAA7D;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC;IAC5B,YAAY;IACZ,eAAe;CAC1C,GAAU,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAC,CAAC,CAoCtE;AApbD;;;;;;;;;;;;;;;;;;;;EA6BC;AAmED,0DAEC;AAkCD,mDAEC;AAnBD,iDAeC;AA9BD,2DAaC;AAuBD,2DAwBC;AAjID,4DA+DC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Sends an email using a predefined template, with optional data/or recipientVariables
3
- * @typedef {{ clientUrl?: string, emailFrom?: string, mailgunDomain?: string, mailgunKey?: string, name?: string }} Config
3
+ * @typedef {{ baseUrl?: string, emailFrom?: string, mailgunDomain?: string, mailgunKey?: string, name?: string }} Config
4
4
  *
5
5
  * @param {object} opts
6
6
  * @param {string} opts.template - Template name or raw HTML, e.g., 'reset-password'
@@ -33,7 +33,7 @@ export function sendEmail({ template, to, config, bcc, data, from, replyTo, reci
33
33
  * Sends an email using a predefined template, with optional data/or recipientVariables
34
34
  */
35
35
  export type Config = {
36
- clientUrl?: string;
36
+ baseUrl?: string;
37
37
  emailFrom?: string;
38
38
  mailgunDomain?: string;
39
39
  mailgunKey?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/email/index.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;GAiBG;AACH,iIAbG;IAAqB,QAAQ,EAArB,MAAM;IACO,EAAE,EAAf,MAAM;IACO,MAAM,EAAnB,MAAM;IACQ,GAAG,GAAjB,MAAM;IACQ,IAAI,GAAlB,MAAM;IACQ,IAAI,GAAlB,MAAM;IACQ,OAAO,GAArB,MAAM;IACQ,kBAAkB,GAAhC,MAAM;IACQ,OAAO,GAArB,MAAM;IACS,aAAa,GAA5B,OAAO;IACQ,IAAI,GAAnB,OAAO;CACf,GAAU,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAiF/B;;;;qBA/FY;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/email/index.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;GAiBG;AACH,iIAbG;IAAqB,QAAQ,EAArB,MAAM;IACO,EAAE,EAAf,MAAM;IACO,MAAM,EAAnB,MAAM;IACQ,GAAG,GAAjB,MAAM;IACQ,IAAI,GAAlB,MAAM;IACQ,IAAI,GAAlB,MAAM;IACQ,OAAO,GAArB,MAAM;IACQ,kBAAkB,GAAhC,MAAM;IACQ,OAAO,GAArB,MAAM;IACS,aAAa,GAA5B,OAAO;IACQ,IAAI,GAAnB,OAAO;CACf,GAAU,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAiF/B;;;;qBA/FY;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE"}
@@ -14,62 +14,70 @@ declare namespace _default {
14
14
  export { required_1 as required };
15
15
  export let index: string;
16
16
  }
17
- export namespace firstName {
17
+ export namespace isInvited {
18
18
  let type_2: string;
19
19
  export { type_2 as type };
20
- let required_2: boolean;
21
- export { required_2 as required };
22
20
  }
23
- export namespace lastName {
21
+ export namespace firstName {
24
22
  let type_3: string;
25
23
  export { type_3 as type };
26
- let required_3: boolean;
27
- export { required_3 as required };
24
+ let required_2: boolean;
25
+ export { required_2 as required };
28
26
  }
29
- export namespace password {
27
+ export namespace lastName {
30
28
  let type_4: string;
31
29
  export { type_4 as type };
32
- export let minLength: number;
30
+ let required_3: boolean;
31
+ export { required_3 as required };
33
32
  }
34
- export namespace resetToken {
33
+ export namespace status {
35
34
  let type_5: string;
36
35
  export { type_5 as type };
37
- }
38
- export namespace status {
39
- let type_6: string;
40
- export { type_6 as type };
41
36
  let _default: string;
42
37
  export { _default as default };
43
38
  let _enum: string[];
44
39
  export { _enum as enum };
45
40
  }
46
41
  export namespace stripeCustomer {
42
+ let type_6: string;
43
+ export { type_6 as type };
44
+ }
45
+ export namespace stripeSubscription {
47
46
  let type_7: string;
48
47
  export { type_7 as type };
49
48
  }
50
- export namespace stripeSubscription {
49
+ export namespace stripeIntents {
51
50
  let type_8: string;
52
51
  export { type_8 as type };
53
52
  }
54
- export namespace stripeIntents {
55
- let type_9: string;
56
- export { type_9 as type };
57
- }
58
- export namespace type_10 {
59
- let type_11: string;
60
- export { type_11 as type };
53
+ export namespace type_9 {
54
+ let type_10: string;
55
+ export { type_10 as type };
61
56
  let _default_1: string;
62
57
  export { _default_1 as default };
63
58
  let _enum_1: string[];
64
59
  export { _enum_1 as enum };
65
60
  }
66
- export { type_10 as type };
61
+ export { type_9 as type };
67
62
  export namespace usedFreeTrial {
68
- let type_12: string;
69
- export { type_12 as type };
63
+ let type_11: string;
64
+ export { type_11 as type };
70
65
  let _default_2: boolean;
71
66
  export { _default_2 as default };
72
67
  }
68
+ export namespace password {
69
+ let type_12: string;
70
+ export { type_12 as type };
71
+ export let minLength: number;
72
+ }
73
+ export namespace inviteToken {
74
+ let type_13: string;
75
+ export { type_13 as type };
76
+ }
77
+ export namespace resetToken {
78
+ let type_14: string;
79
+ export { type_14 as type };
80
+ }
73
81
  }
74
82
  let findBL: string[];
75
83
  let updateBL: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../server/models/user.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8CmB,gCAEd"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../server/models/user.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiDmB,gCAEd"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../server/router.js"],"names":[],"mappings":"AAmCA,wHA6HC;AAoPD,+CAEC;AAED,kEAYC;AApGD,+BAA+B;AAC/B,yBADW,gBAAgB,CAkF1B;sBAnYY,OAAO,CAAC,OAAO,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAoB,CAAC;CAC7B;uBACS,OAAO,CAAC,QAAQ,GAAG;IAC3B,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACvD,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;CACvD;+BACS;IACR,KAAK,EAAE,MAAM,EAAE,CAAC;IACpB,CAAK,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,UAAU,KAAK,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;CACnF;iBA1Ba,MAAM;oBAIH,SAAS"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../server/router.js"],"names":[],"mappings":"AAoCA,wHA6HC;AAoPD,+CAEC;AAED,kEAYC;AApGD,+BAA+B;AAC/B,yBADW,gBAAgB,CAkF1B;sBAnYY,OAAO,CAAC,OAAO,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAoB,CAAC;CAC7B;uBACS,OAAO,CAAC,QAAQ,GAAG;IAC3B,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACvD,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;CACvD;+BACS;IACR,KAAK,EAAE,MAAM,EAAE,CAAC;IACpB,CAAK,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,UAAU,KAAK,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;CACnF;iBA1Ba,MAAM;oBAIH,SAAS"}
package/types.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  type InjectedConfig = {
4
4
  awsUrl?: string
5
- clientUrl: string
5
+ baseUrl: string
6
6
  env: string
7
7
  googleMapsApiKey?: string
8
8
  isDemo: boolean // implicitly defined by webpack
@@ -29,6 +29,7 @@ export type User = {
29
29
  name?: string
30
30
  avatar?: MonasteryImage
31
31
  isAdmin?: boolean
32
+ isInvited?: boolean
32
33
  type?: string
33
34
  }
34
35
 
package/util.js CHANGED
@@ -103,9 +103,9 @@ export function axios ({ createConfig } = {}) {
103
103
  if (typeof window !== 'undefined') {
104
104
  if (!axiosNonce) {
105
105
  // Remove mobile specific protocol and subdomain
106
- const clientOrigin = window.document.location.origin.replace(/^(capacitor|https):\/\/(mobile\.)?/, 'https://')
106
+ const baseUrl = window.document.location.origin.replace(/^(capacitor|https):\/\/(mobile\.)?/, 'https://')
107
107
  axiosNonce = true
108
- _axios.defaults.baseURL = clientOrigin
108
+ _axios.defaults.baseURL = baseUrl
109
109
  _axios.defaults.headers.desktop = true
110
110
  _axios.defaults.withCredentials = true
111
111
  _axios.defaults.timeout = 60000