nitro-web 0.0.62 → 0.0.64
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/components/auth/auth.api.js +102 -56
- package/components/billing/stripe.api.js +81 -83
- package/components/partials/element/button.tsx +8 -3
- package/components/partials/element/message.tsx +11 -11
- package/components/partials/element/modal.tsx +9 -8
- package/components/partials/form/field.tsx +2 -2
- package/components/partials/form/form-error.tsx +1 -1
- package/components/partials/form/select.tsx +5 -5
- package/components/partials/styleguide.tsx +1 -1
- package/components/settings/settings.api.js +3 -7
- package/package.json +2 -2
- package/server/email/invite-user.html +23 -0
- package/server/index.js +9 -13
- package/server/router.js +22 -33
- package/types/util.d.ts +5 -4
- package/types/util.d.ts.map +1 -1
- package/util.js +6 -4
|
@@ -11,28 +11,31 @@ import { isArray, pick, isString, ucFirst, fullNameSplit } from 'nitro-web/util'
|
|
|
11
11
|
let authConfig = null
|
|
12
12
|
const JWT_SECRET = process.env.JWT_SECRET || 'replace_this_with_secure_env_secret'
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
export const routes = {
|
|
15
|
+
// Routes
|
|
16
|
+
'get /api/store': [store],
|
|
17
|
+
'get /api/signout': [signout],
|
|
18
|
+
'post /api/signin': [signin],
|
|
19
|
+
'post /api/signup': [signup],
|
|
20
|
+
'post /api/reset-instructions': [resetInstructions],
|
|
21
|
+
'post /api/reset-password': [resetPassword],
|
|
22
|
+
'post /api/invite-instructions': [inviteInstructions],
|
|
23
|
+
'post /api/invite-accept': [resetPassword],
|
|
24
|
+
'delete /api/account/:uid': [remove],
|
|
25
|
+
|
|
26
|
+
// Overridable helpers
|
|
24
27
|
setup: setup,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
findUserFromProvider: findUserFromProvider,
|
|
29
|
+
getStore: getStore,
|
|
30
|
+
signinAndGetStore: signinAndGetStore,
|
|
31
|
+
tokenCreate: tokenCreate,
|
|
32
|
+
tokenParse: tokenParse,
|
|
33
|
+
userCreate: userCreate,
|
|
34
|
+
validatePassword: validatePassword,
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
function setup(middleware, _config) {
|
|
35
|
-
//
|
|
38
|
+
// routes.setup is called automatically when express starts
|
|
36
39
|
// Set config values
|
|
37
40
|
const configKeys = ['clientUrl', 'emailFrom', 'env', 'name', 'mailgunDomain', 'mailgunKey', 'masterPassword', 'isNotMultiTenant']
|
|
38
41
|
authConfig = pick(_config, configKeys)
|
|
@@ -45,7 +48,7 @@ function setup(middleware, _config) {
|
|
|
45
48
|
{ usernameField: 'email' },
|
|
46
49
|
async (email, password, next) => {
|
|
47
50
|
try {
|
|
48
|
-
const user = await findUserFromProvider({ email }, password)
|
|
51
|
+
const user = await this.findUserFromProvider({ email }, password)
|
|
49
52
|
next(null, user)
|
|
50
53
|
} catch (err) {
|
|
51
54
|
next(err.message)
|
|
@@ -62,7 +65,7 @@ function setup(middleware, _config) {
|
|
|
62
65
|
},
|
|
63
66
|
async (payload, done) => {
|
|
64
67
|
try {
|
|
65
|
-
const user = await findUserFromProvider({ _id: payload._id })
|
|
68
|
+
const user = await this.findUserFromProvider({ _id: payload._id })
|
|
66
69
|
if (!user) return done(null, false)
|
|
67
70
|
return done(null, user)
|
|
68
71
|
} catch (err) {
|
|
@@ -97,24 +100,26 @@ function setup(middleware, _config) {
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
async function store(req, res) {
|
|
100
|
-
res.json(await getStore(req.user))
|
|
103
|
+
res.json(await this.getStore(req.user))
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
async function signup(req, res) {
|
|
104
107
|
try {
|
|
105
|
-
|
|
108
|
+
const desktop = req.query.desktop
|
|
109
|
+
let user = await this.userCreate(req.body, this.findUserFromProvider)
|
|
106
110
|
sendEmail({
|
|
107
111
|
config: authConfig,
|
|
108
112
|
template: 'welcome',
|
|
109
113
|
to: `${ucFirst(user.firstName)}<${user.email}>`,
|
|
110
114
|
}).catch(console.error)
|
|
111
|
-
res.send(await
|
|
115
|
+
res.send(await this.signinAndGetStore(user, desktop, this.getStore))
|
|
112
116
|
} catch (err) {
|
|
113
117
|
res.error(err)
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
function signin(req, res) {
|
|
122
|
+
const desktop = req.query.desktop
|
|
118
123
|
if (!req.body.email) return res.error('email', 'The email you entered is incorrect.')
|
|
119
124
|
if (!req.body.password) return res.error('password', 'The password you entered is incorrect.')
|
|
120
125
|
|
|
@@ -122,7 +127,7 @@ function signin(req, res) {
|
|
|
122
127
|
if (err) return res.error(err)
|
|
123
128
|
if (!user && info) return res.error('email', info.message)
|
|
124
129
|
try {
|
|
125
|
-
const response = await
|
|
130
|
+
const response = await this.signinAndGetStore(user, desktop, this.getStore)
|
|
126
131
|
res.send(response)
|
|
127
132
|
} catch (err) {
|
|
128
133
|
res.error(err)
|
|
@@ -137,7 +142,7 @@ function signout(req, res) {
|
|
|
137
142
|
async function resetInstructions(req, res) {
|
|
138
143
|
try {
|
|
139
144
|
let email = (req.body.email || '').trim().toLowerCase()
|
|
140
|
-
if (!email
|
|
145
|
+
if (!email) throw { title: 'email', detail: 'The email you entered is incorrect.' }
|
|
141
146
|
|
|
142
147
|
let user = await db.user.findOne({ query: { email }, _privateData: true })
|
|
143
148
|
if (!user) throw { title: 'email', detail: 'The email you entered is incorrect.' }
|
|
@@ -162,11 +167,13 @@ async function resetInstructions(req, res) {
|
|
|
162
167
|
async function resetPassword(req, res) {
|
|
163
168
|
try {
|
|
164
169
|
const { token, password, password2 } = req.body
|
|
170
|
+
const name = req.path.includes('invite') ? 'inviteToken' : 'resetToken'
|
|
171
|
+
const desktop = req.query.desktop
|
|
165
172
|
const id = tokenParse(token)
|
|
166
173
|
await validatePassword(password, password2)
|
|
167
174
|
|
|
168
|
-
let user = await db.user.findOne({ query: id, blacklist: ['-
|
|
169
|
-
if (!user || user
|
|
175
|
+
let user = await db.user.findOne({ query: id, blacklist: ['-' + name], _privateData: true })
|
|
176
|
+
if (!user || user[name] !== token) throw new Error('Sorry your token is invalid or has already been used.')
|
|
170
177
|
|
|
171
178
|
await db.user.update({
|
|
172
179
|
query: user._id,
|
|
@@ -174,14 +181,53 @@ async function resetPassword(req, res) {
|
|
|
174
181
|
password: await bcrypt.hash(password, 10),
|
|
175
182
|
resetToken: '',
|
|
176
183
|
},
|
|
177
|
-
blacklist: ['-
|
|
184
|
+
blacklist: ['-' + name, '-password'],
|
|
178
185
|
})
|
|
179
|
-
res.send(await
|
|
186
|
+
res.send(await this.signinAndGetStore({ ...user, [name]: undefined }, desktop, this.getStore))
|
|
180
187
|
} catch (err) {
|
|
181
188
|
res.error(err)
|
|
182
189
|
}
|
|
183
190
|
}
|
|
184
191
|
|
|
192
|
+
async function inviteInstructions(req, res) {
|
|
193
|
+
try {
|
|
194
|
+
// Check if user is admin here rather than in middleware (which may not exist yet)
|
|
195
|
+
if (req.user.type != 'admin') {
|
|
196
|
+
throw new Error('You are not authorized to invite users.')
|
|
197
|
+
}
|
|
198
|
+
const inviteToken = await tokenCreate()
|
|
199
|
+
const userData = await db.user.validate({
|
|
200
|
+
...pick(req.body, ['email', 'firstName', 'lastName']),
|
|
201
|
+
status: 'invited',
|
|
202
|
+
inviteToken: inviteToken,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
// Check if user already exists
|
|
206
|
+
if (await db.user.findOne({ query: { email: userData.email } })) {
|
|
207
|
+
throw { title: 'email', detail: 'User already exists.' }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Create user
|
|
211
|
+
const user = await db.user.insert({
|
|
212
|
+
data: userData,
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// Send email
|
|
216
|
+
res.send(user)
|
|
217
|
+
sendEmail({
|
|
218
|
+
config: authConfig,
|
|
219
|
+
template: 'invite-user',
|
|
220
|
+
to: `${ucFirst(userData.firstName)}<${userData.email}>`,
|
|
221
|
+
data: {
|
|
222
|
+
token: inviteToken + (req.query.hasOwnProperty('desktop') ? '?desktop' : ''),
|
|
223
|
+
},
|
|
224
|
+
}).catch(err => console.error('sendEmail(..) mailgun error', err))
|
|
225
|
+
|
|
226
|
+
} catch (err) {
|
|
227
|
+
return res.error(err)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
185
231
|
async function remove(req, res) {
|
|
186
232
|
try {
|
|
187
233
|
const uid = db.id(req.params.uid || 'badid')
|
|
@@ -208,7 +254,7 @@ async function remove(req, res) {
|
|
|
208
254
|
}
|
|
209
255
|
}
|
|
210
256
|
|
|
211
|
-
/* ----
|
|
257
|
+
/* ---- Overridable helpers ------------------ */
|
|
212
258
|
|
|
213
259
|
export async function findUserFromProvider(query, passwordToCheck) {
|
|
214
260
|
/**
|
|
@@ -260,7 +306,7 @@ export async function getStore(user) {
|
|
|
260
306
|
}
|
|
261
307
|
}
|
|
262
308
|
|
|
263
|
-
export async function
|
|
309
|
+
export async function signinAndGetStore(user, isDesktop, getStore) {
|
|
264
310
|
if (user.loginActive === false) throw 'This user is not available.'
|
|
265
311
|
user.desktop = isDesktop
|
|
266
312
|
|
|
@@ -269,30 +315,7 @@ export async function signinAndGetState(user, isDesktop) {
|
|
|
269
315
|
return { ...store, jwt }
|
|
270
316
|
}
|
|
271
317
|
|
|
272
|
-
export function
|
|
273
|
-
return new Promise((resolve) => {
|
|
274
|
-
crypto.randomBytes(16, (err, buff) => {
|
|
275
|
-
let hash = buff.toString('hex') // 32 chars
|
|
276
|
-
resolve(`${hash}${id || ''}:${Date.now()}`)
|
|
277
|
-
})
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export function tokenParse(token) {
|
|
282
|
-
let split = (token || '').split(':')
|
|
283
|
-
let hash = split[0].slice(0, 32)
|
|
284
|
-
let userId = split[0].slice(32)
|
|
285
|
-
let time = split[1]
|
|
286
|
-
if (!hash || !userId || !time) {
|
|
287
|
-
throw { title: 'error', detail: 'Sorry your code is invalid.' }
|
|
288
|
-
} else if (parseFloat(time) + 1000 * 60 * 60 * 24 < Date.now()) {
|
|
289
|
-
throw { title: 'error', detail: 'Sorry your code has timed out.' }
|
|
290
|
-
} else {
|
|
291
|
-
return userId
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export async function userCreate({ name, business, email, password }) {
|
|
318
|
+
export async function userCreate({ name, business, email, password, findUserFromProvider }) {
|
|
296
319
|
try {
|
|
297
320
|
const options = { blacklist: ['-_id'] }
|
|
298
321
|
const isMultiTenant = !authConfig.isNotMultiTenant
|
|
@@ -342,6 +365,29 @@ export async function userCreate({ name, business, email, password }) {
|
|
|
342
365
|
}
|
|
343
366
|
}
|
|
344
367
|
|
|
368
|
+
export function tokenCreate(id) {
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
crypto.randomBytes(16, (err, buff) => {
|
|
371
|
+
let hash = buff.toString('hex') // 32 chars
|
|
372
|
+
resolve(`${hash}${id || ''}:${Date.now()}`)
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function tokenParse(token) {
|
|
378
|
+
let split = (token || '').split(':')
|
|
379
|
+
let hash = split[0].slice(0, 32)
|
|
380
|
+
let userId = split[0].slice(32)
|
|
381
|
+
let time = split[1]
|
|
382
|
+
if (!hash || !userId || !time) {
|
|
383
|
+
throw { title: 'error', detail: 'Sorry your code is invalid.' }
|
|
384
|
+
} else if (parseFloat(time) + 1000 * 60 * 60 * 24 < Date.now()) {
|
|
385
|
+
throw { title: 'error', detail: 'Sorry your code has timed out.' }
|
|
386
|
+
} else {
|
|
387
|
+
return userId
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
345
391
|
export async function validatePassword(password='', password2) {
|
|
346
392
|
// let hasLowerChar = password.match(/[a-z]/)
|
|
347
393
|
// let hasUpperChar = password.match(/[A-Z]/)
|
|
@@ -359,4 +405,4 @@ export async function validatePassword(password='', password2) {
|
|
|
359
405
|
} else if (typeof password2 != 'undefined' && password !== password2) {
|
|
360
406
|
throw [{ title: 'password2', detail: 'Your passwords need to match.' }]
|
|
361
407
|
}
|
|
362
|
-
}
|
|
408
|
+
}
|
|
@@ -7,16 +7,14 @@ let stripe = undefined
|
|
|
7
7
|
let stripeProducts = []
|
|
8
8
|
let config = {}
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export const routes = {
|
|
11
|
+
// Routes
|
|
12
|
+
'post /api/stripe/webhook': [stripeWebhook],
|
|
13
|
+
'post /api/stripe/create-billing-portal-session': [billingPortalSessionCreate],
|
|
14
|
+
'get /api/stripe/upcoming-invoices': [upcomingInvoicesFind],
|
|
15
|
+
|
|
16
|
+
// Overridable helpers
|
|
16
17
|
setup: setup,
|
|
17
|
-
stripeWebhook: stripeWebhook,
|
|
18
|
-
billingPortalSessionCreate: billingPortalSessionCreate,
|
|
19
|
-
upcomingInvoicesFind: upcomingInvoicesFind,
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
function setup(middleware, _config) {
|
|
@@ -104,7 +102,80 @@ async function upcomingInvoicesFind(req, res) {
|
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
/*
|
|
105
|
+
/* ---- Overridable helpers ------------------ */
|
|
106
|
+
|
|
107
|
+
async function error(req, res, err) {
|
|
108
|
+
if (err && err.response && err.response.body) console.log(err.response.body)
|
|
109
|
+
if (util.isString(err) && err.match(/Cannot find company with id/)) {
|
|
110
|
+
res.json({ user: 'no company found' })
|
|
111
|
+
} else res.error(err)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function getProducts() {
|
|
115
|
+
/**
|
|
116
|
+
* Returns all products and caches it on the app
|
|
117
|
+
* @returns {Array} products
|
|
118
|
+
*/
|
|
119
|
+
try {
|
|
120
|
+
if (stripeProducts) return stripeProducts
|
|
121
|
+
if (!config.stripeSecretKey) {
|
|
122
|
+
stripeProducts = []
|
|
123
|
+
throw new Error('Missing process.env.stripeSecretKey for retrieving products')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let products = (await stripe.products.list({ limit: 100, active: true })).data
|
|
127
|
+
let prices = (await stripe.prices.list({ limit: 100, active: true, expand: ['data.tiers'] })).data
|
|
128
|
+
|
|
129
|
+
return (stripeProducts = products.map((product) => ({
|
|
130
|
+
// remove default_price when new pricing is ready
|
|
131
|
+
...util.pick(product, ['id', 'created', 'default_price', 'description', 'name', 'metadata']),
|
|
132
|
+
type: product.name.match(/housing/i) ? 'project' : 'subscription', // overwriting, was 'service'
|
|
133
|
+
prices: prices
|
|
134
|
+
.filter((price) => price.product == product.id)
|
|
135
|
+
.map((price) => ({
|
|
136
|
+
...util.pick(price, ['id', 'product', 'nickname', 'recurring', 'unit_amount', 'tiers', 'tiers_mode']),
|
|
137
|
+
interval: price.recurring?.interval, // 'year', 'month', undefined
|
|
138
|
+
})),
|
|
139
|
+
})))
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(new Error(err)) // when stripe throws errors, the callstack is missing.
|
|
142
|
+
return []
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function getUserFromEvent(event) {
|
|
147
|
+
// User retreived from the event's customer.
|
|
148
|
+
// The customer is created before the paymentIntent and subscriptionIntent is set up
|
|
149
|
+
let object = event.data.object
|
|
150
|
+
let customerId = object.object == 'customer'? object.id : object.customer
|
|
151
|
+
if (customerId) {
|
|
152
|
+
var user = await db.user.findOne({
|
|
153
|
+
query: { 'stripeCustomer.id': customerId },
|
|
154
|
+
populate: db.user.populate({}),
|
|
155
|
+
blacklist: false, // ['-company.users.inviteToken'],
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
if (!user) {
|
|
159
|
+
await db.log.insert({ data: {
|
|
160
|
+
date: Date.now(),
|
|
161
|
+
event: event.type,
|
|
162
|
+
message: `Cannot find user with id: ${customerId}.`,
|
|
163
|
+
}})
|
|
164
|
+
throw new Error(`Cannot find user with id: ${customerId}.`)
|
|
165
|
+
}
|
|
166
|
+
// populate company owner with user data (handy for _addSubscriptionBillingChange)
|
|
167
|
+
if (user.company?.users) {
|
|
168
|
+
user.company.users = user.company.users.map(o => {
|
|
169
|
+
if (o.role == 'owner' && o._id.toString() == user._id.toString()) {
|
|
170
|
+
o.firstName = user.firstName
|
|
171
|
+
o.name = user.name
|
|
172
|
+
o.email = user.email
|
|
173
|
+
}
|
|
174
|
+
return o
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
return user
|
|
178
|
+
}
|
|
108
179
|
|
|
109
180
|
async function webhookCustomerCreatedUpdated(req, res, event) {
|
|
110
181
|
try {
|
|
@@ -169,72 +240,6 @@ async function webhookSubUpdated(req, res, event) {
|
|
|
169
240
|
}
|
|
170
241
|
}
|
|
171
242
|
|
|
172
|
-
async function getUserFromEvent(event) {
|
|
173
|
-
// User retreived from the event's customer.
|
|
174
|
-
// The customer is created before the paymentIntent and subscriptionIntent is set up
|
|
175
|
-
let object = event.data.object
|
|
176
|
-
let customerId = object.object == 'customer'? object.id : object.customer
|
|
177
|
-
if (customerId) {
|
|
178
|
-
var user = await db.user.findOne({
|
|
179
|
-
query: { 'stripeCustomer.id': customerId },
|
|
180
|
-
populate: db.user.populate({}),
|
|
181
|
-
blacklist: false, // ['-company.users.inviteToken'],
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
if (!user) {
|
|
185
|
-
await db.log.insert({ data: {
|
|
186
|
-
date: Date.now(),
|
|
187
|
-
event: event.type,
|
|
188
|
-
message: `Cannot find user with id: ${customerId}.`,
|
|
189
|
-
}})
|
|
190
|
-
throw new Error(`Cannot find user with id: ${customerId}.`)
|
|
191
|
-
}
|
|
192
|
-
// populate company owner with user data (handy for _addSubscriptionBillingChange)
|
|
193
|
-
if (user.company?.users) {
|
|
194
|
-
user.company.users = user.company.users.map(o => {
|
|
195
|
-
if (o.role == 'owner' && o._id.toString() == user._id.toString()) {
|
|
196
|
-
o.firstName = user.firstName
|
|
197
|
-
o.name = user.name
|
|
198
|
-
o.email = user.email
|
|
199
|
-
}
|
|
200
|
-
return o
|
|
201
|
-
})
|
|
202
|
-
}
|
|
203
|
-
return user
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export async function getProducts() {
|
|
207
|
-
/**
|
|
208
|
-
* Returns all products and caches it on the app
|
|
209
|
-
* @returns {Array} products
|
|
210
|
-
*/
|
|
211
|
-
try {
|
|
212
|
-
if (stripeProducts) return stripeProducts
|
|
213
|
-
if (!config.stripeSecretKey) {
|
|
214
|
-
stripeProducts = []
|
|
215
|
-
throw new Error('Missing process.env.stripeSecretKey for retrieving products')
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
let products = (await stripe.products.list({ limit: 100, active: true })).data
|
|
219
|
-
let prices = (await stripe.prices.list({ limit: 100, active: true, expand: ['data.tiers'] })).data
|
|
220
|
-
|
|
221
|
-
return (stripeProducts = products.map((product) => ({
|
|
222
|
-
// remove default_price when new pricing is ready
|
|
223
|
-
...util.pick(product, ['id', 'created', 'default_price', 'description', 'name', 'metadata']),
|
|
224
|
-
type: product.name.match(/housing/i) ? 'project' : 'subscription', // overwriting, was 'service'
|
|
225
|
-
prices: prices
|
|
226
|
-
.filter((price) => price.product == product.id)
|
|
227
|
-
.map((price) => ({
|
|
228
|
-
...util.pick(price, ['id', 'product', 'nickname', 'recurring', 'unit_amount', 'tiers', 'tiers_mode']),
|
|
229
|
-
interval: price.recurring?.interval, // 'year', 'month', undefined
|
|
230
|
-
})),
|
|
231
|
-
})))
|
|
232
|
-
} catch (err) {
|
|
233
|
-
console.error(new Error(err)) // when stripe throws errors, the callstack is missing.
|
|
234
|
-
return []
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
243
|
// async function createOrUpdateCustomer(user, paymentMethod=null) {
|
|
239
244
|
// /**
|
|
240
245
|
// * Creates or updates a stripe customer and saves it to the user
|
|
@@ -261,10 +266,3 @@ export async function getProducts() {
|
|
|
261
266
|
// blacklist: ['-stripeCustomer'],
|
|
262
267
|
// })
|
|
263
268
|
// }
|
|
264
|
-
|
|
265
|
-
async function error(req, res, err) {
|
|
266
|
-
if (err && err.response && err.response.body) console.log(err.response.body)
|
|
267
|
-
if (util.isString(err) && err.match(/Cannot find company with id/)) {
|
|
268
|
-
res.json({ user: 'no company found' })
|
|
269
|
-
} else res.error(err)
|
|
270
|
-
}
|
|
@@ -24,7 +24,8 @@ export function Button({
|
|
|
24
24
|
IconLeftEnd,
|
|
25
25
|
IconRight,
|
|
26
26
|
IconRightEnd,
|
|
27
|
-
children,
|
|
27
|
+
children,
|
|
28
|
+
type='button',
|
|
28
29
|
...props
|
|
29
30
|
}: Button) {
|
|
30
31
|
// const size = (color.match(/xs|sm|md|lg/)?.[0] || 'md') as 'xs'|'sm'|'md'|'lg'
|
|
@@ -37,7 +38,7 @@ export function Button({
|
|
|
37
38
|
const colors = {
|
|
38
39
|
'primary': 'bg-primary hover:bg-primary-hover ring-transparent text-white [&>.loader]:border-white',
|
|
39
40
|
'secondary': 'bg-secondary hover:bg-secondary-hover ring-transparent text-white [&>.loader]:border-white',
|
|
40
|
-
'black': 'bg-black hover:bg-gray-
|
|
41
|
+
'black': 'bg-black hover:bg-gray-800 ring-transparent text-white [&>.loader]:border-white',
|
|
41
42
|
'dark': 'bg-gray-800 hover:bg-gray-700 ring-transparent text-white [&>.loader]:border-white',
|
|
42
43
|
'white': 'bg-white hover:bg-gray-50 ring-gray-300 text-gray-900 [&>.loader]:border-black', // maybe change to text-foreground
|
|
43
44
|
'clear': 'hover:bg-gray-50 ring-gray-300 text-foreground [&>.loader]:border-foreground !shadow-none',
|
|
@@ -62,7 +63,11 @@ export function Button({
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
return (
|
|
65
|
-
<button
|
|
66
|
+
<button
|
|
67
|
+
type={type}
|
|
68
|
+
class={twMerge(`${base} ${sizes[size]} ${appliedColor} ${contentLayout} ${loading} nitro-button ${className||''}`)}
|
|
69
|
+
{...props}
|
|
70
|
+
>
|
|
66
71
|
{IconLeft && getIcon(IconLeft)}
|
|
67
72
|
{IconLeftEnd && getIcon(IconLeftEnd)}
|
|
68
73
|
<span class={`${iconPosition == 'leftEnd' || iconPosition == 'rightEnd' ? 'flex-1' : ''}`}>{children}</span>
|
|
@@ -25,8 +25,8 @@ export function Message({ className }: { className?: string }) {
|
|
|
25
25
|
'unauth': { type: 'error', text: 'You are unauthorised' },
|
|
26
26
|
}
|
|
27
27
|
const colorMap = {
|
|
28
|
-
'error': 'text-
|
|
29
|
-
'warning': 'text-
|
|
28
|
+
'error': 'text-danger',
|
|
29
|
+
'warning': 'text-warning',
|
|
30
30
|
'info': 'text-info',
|
|
31
31
|
'success': 'text-success',
|
|
32
32
|
}
|
|
@@ -70,7 +70,7 @@ export function Message({ className }: { className?: string }) {
|
|
|
70
70
|
// Show message and hide it again after some time. Send back cleanup if store.message changes
|
|
71
71
|
} else if (messageObject && now - 500 < messageObject.date) {
|
|
72
72
|
const timeout1 = setTimeout(() => setVisible(true), 50)
|
|
73
|
-
if (messageObject.timeout !== 0 && !devDontHide) var timeout2 = setTimeout(hide, messageObject.timeout ||
|
|
73
|
+
if (messageObject.timeout !== 0 && !devDontHide) var timeout2 = setTimeout(hide, messageObject.timeout || 5000000)
|
|
74
74
|
return () => {
|
|
75
75
|
clearTimeout(timeout1)
|
|
76
76
|
clearTimeout(timeout2)
|
|
@@ -97,24 +97,24 @@ export function Message({ className }: { className?: string }) {
|
|
|
97
97
|
(visible ? 'translate-x-0 opacity-100' : 'translate-x-1 opacity-0')
|
|
98
98
|
}>
|
|
99
99
|
<div className="p-3">
|
|
100
|
-
<div className="flex items-start">
|
|
101
|
-
<div className="shrink-0">
|
|
102
|
-
<CircleCheck aria-hidden="true" size={
|
|
100
|
+
<div className="flex items-start gap-3 text-sm leading-[1.4em]">
|
|
101
|
+
<div className="flex items-center shrink-0 min-h-[1.4em]">
|
|
102
|
+
<CircleCheck aria-hidden="true" size={19} className={`${color}`} />
|
|
103
103
|
</div>
|
|
104
|
-
<div className="
|
|
105
|
-
<p className="
|
|
104
|
+
<div className="flex flex-1 items-center min-h-[1.4em]">
|
|
105
|
+
<p className="font-medium text-gray-900">{typeof store.message === 'object' && store.message?.text}
|
|
106
106
|
</p>
|
|
107
107
|
{/* <p className="mt-1 text-sm text-gray-500">{store.message.text}</p> */}
|
|
108
108
|
</div>
|
|
109
|
-
<div className="
|
|
109
|
+
<div className="flex items-center shrink-0 min-h-[1.4em]">
|
|
110
110
|
<button
|
|
111
111
|
type="button"
|
|
112
112
|
onClick={hide}
|
|
113
|
-
className="inline-flex
|
|
113
|
+
className="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2
|
|
114
114
|
focus:ring-indigo-500 focus:ring-offset-2"
|
|
115
115
|
>
|
|
116
116
|
<span className="sr-only">Close</span>
|
|
117
|
-
<X aria-hidden="true" size={19}
|
|
117
|
+
<X aria-hidden="true" size={19} />
|
|
118
118
|
</button>
|
|
119
119
|
</div>
|
|
120
120
|
</div>
|
|
@@ -5,13 +5,15 @@ type ModalProps = {
|
|
|
5
5
|
show: boolean
|
|
6
6
|
setShow: (show: boolean) => void
|
|
7
7
|
children: React.ReactNode
|
|
8
|
+
className?: string
|
|
9
|
+
rootClassName?: string
|
|
10
|
+
dismissable?: boolean
|
|
8
11
|
maxWidth?: string
|
|
9
12
|
minHeight?: string
|
|
10
|
-
dismissable?: boolean
|
|
11
13
|
[key: string]: unknown
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
export function Modal({ show, setShow, children, maxWidth, minHeight, dismissable = true,
|
|
16
|
+
export function Modal({ show, setShow, children, maxWidth, minHeight, dismissable = true, className, rootClassName }: ModalProps) {
|
|
15
17
|
const [state, setState] = useState(show ? 'open' : 'close')
|
|
16
18
|
const containerEl = useRef<HTMLDivElement>(null)
|
|
17
19
|
const isFirst = IsFirstRender()
|
|
@@ -33,8 +35,7 @@ export function Modal({ show, setShow, children, maxWidth, minHeight, dismissabl
|
|
|
33
35
|
container: 'opacity-100 scale-[1] duration-200',
|
|
34
36
|
},
|
|
35
37
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
+
const stateObj = states[state as keyof typeof states]
|
|
38
39
|
|
|
39
40
|
useEffect(() => {
|
|
40
41
|
if (isFirst) return
|
|
@@ -64,15 +65,15 @@ export function Modal({ show, setShow, children, maxWidth, minHeight, dismissabl
|
|
|
64
65
|
return (
|
|
65
66
|
<div
|
|
66
67
|
onClick={(e) => e.stopPropagation()}
|
|
67
|
-
class={`${twMerge(`fixed top-0 w-[100vw] h-[100vh] z-[100] ${
|
|
68
|
+
class={`${twMerge(`fixed top-0 w-[100vw] h-[100vh] z-[100] ${stateObj.root} ${rootClassName||''}`)} nitro-modal`}
|
|
68
69
|
>
|
|
69
|
-
<div class={`!absolute inset-0 box-content bg-gray-500/70 transition-opacity ${
|
|
70
|
-
<div class={`relative h-[100vh] overflow-y-auto transition-[opacity,transform] ${
|
|
70
|
+
<div class={`!absolute inset-0 box-content bg-gray-500/70 transition-opacity ${stateObj.bg}`}></div>
|
|
71
|
+
<div class={`relative h-[100vh] overflow-y-auto transition-[opacity,transform] ${stateObj.container}`}>
|
|
71
72
|
<div class="flex items-center justify-center min-h-full" onMouseDown={onClick}>
|
|
72
73
|
<div
|
|
73
74
|
ref={containerEl}
|
|
74
75
|
style={{ maxWidth: maxWidth || '550px', minHeight: minHeight }}
|
|
75
|
-
class={`relative w-full mx-6 mt-4 mb-8 bg-white rounded-lg shadow-lg ${
|
|
76
|
+
class={`relative w-full mx-6 mt-4 mb-8 bg-white rounded-lg shadow-lg p-9 ${className||''}`}
|
|
76
77
|
>
|
|
77
78
|
<div
|
|
78
79
|
class="absolute top-0 right-0 p-3 m-1 cursor-pointer"
|
|
@@ -133,7 +133,7 @@ function FieldContainer({ children, className, error }: { children: React.ReactN
|
|
|
133
133
|
return (
|
|
134
134
|
<div css={style} className={twMerge(`mt-2.5 mb-6 mt-input-before mb-input-after grid grid-cols-1 nitro-field ${className || ''}`)}>
|
|
135
135
|
{children}
|
|
136
|
-
{error && <div class="mt-1.5 text-xs text-danger nitro-error">{error.detail}</div>}
|
|
136
|
+
{error && <div class="mt-1.5 text-xs text-danger-foreground nitro-error">{error.detail}</div>}
|
|
137
137
|
</div>
|
|
138
138
|
)
|
|
139
139
|
}
|
|
@@ -149,7 +149,7 @@ function getInputClasses({ error, Icon, iconPos, type }: { error?: Error, Icon?:
|
|
|
149
149
|
'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 ' +
|
|
150
150
|
(iconPos == 'right' && Icon ? `${pl} ${prWithIcon} ` : (Icon ? `${plWithIcon} ${pr} ` : `${pl} ${pr} `)) +
|
|
151
151
|
(error
|
|
152
|
-
? 'text-
|
|
152
|
+
? 'text-danger-foreground outline-danger focus:outline-danger '
|
|
153
153
|
: 'text-input outline-input-border focus:outline-input-border-focus ') +
|
|
154
154
|
(iconPos == 'right' ? 'justify-self-start ' : 'justify-self-end ') +
|
|
155
155
|
'nitro-input'
|
|
@@ -18,7 +18,7 @@ export function FormError({ state, fields, className }: FormError) {
|
|
|
18
18
|
return (
|
|
19
19
|
<>
|
|
20
20
|
{error ? (
|
|
21
|
-
<div class={`text-danger mt-1 text-sm nitro-error ${className||''}`}>
|
|
21
|
+
<div class={`text-danger-foreground mt-1 text-sm nitro-error ${className||''}`}>
|
|
22
22
|
{error.detail}
|
|
23
23
|
</div>
|
|
24
24
|
) : null}
|
|
@@ -151,7 +151,7 @@ function SelectBase({ inputId, minMenuWidth, name, prefix='', onChange, options,
|
|
|
151
151
|
// isDisabled={true}
|
|
152
152
|
// maxMenuHeight={200}
|
|
153
153
|
/>
|
|
154
|
-
{error && <div class="mt-1.5 text-xs text-danger">{error.detail}</div>}
|
|
154
|
+
{error && <div class="mt-1.5 text-xs text-danger-foreground">{error.detail}</div>}
|
|
155
155
|
</div>
|
|
156
156
|
)
|
|
157
157
|
}
|
|
@@ -257,7 +257,7 @@ const selectStyles = {
|
|
|
257
257
|
// Input container objects
|
|
258
258
|
input: {
|
|
259
259
|
base: 'text-input',
|
|
260
|
-
error: 'text-
|
|
260
|
+
error: 'text-danger-foreground',
|
|
261
261
|
},
|
|
262
262
|
multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
263
263
|
multiValueLabel: 'text-xs',
|
|
@@ -265,10 +265,10 @@ const selectStyles = {
|
|
|
265
265
|
placeholder: 'text-input-placeholder',
|
|
266
266
|
singleValue: {
|
|
267
267
|
base: 'text-input',
|
|
268
|
-
error: 'text-
|
|
268
|
+
error: 'text-danger-foreground',
|
|
269
269
|
},
|
|
270
270
|
// Icon indicators
|
|
271
|
-
clearIndicator: 'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-
|
|
271
|
+
clearIndicator: 'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-danger-foreground',
|
|
272
272
|
dropdownIndicator: 'p-1 hover:bg-gray-100 text-gray-500 rounded-md hover:text-black',
|
|
273
273
|
indicatorsContainer: 'p-1 px-2 gap-1',
|
|
274
274
|
indicatorSeparator: 'py-0.5 before:content-[""] before:block before:bg-gray-100 before:w-px before:h-full',
|
|
@@ -279,7 +279,7 @@ const selectStyles = {
|
|
|
279
279
|
option: {
|
|
280
280
|
base: 'relative px-3 py-2 !flex items-center gap-2 cursor-default',
|
|
281
281
|
hover: 'bg-gray-50',
|
|
282
|
-
selected: '!bg-gray-100 text-
|
|
282
|
+
selected: '!bg-gray-100 text-dropdown-selected-foreground',
|
|
283
283
|
},
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -348,7 +348,7 @@ export function Styleguide({ className, elements, children }: StyleguideProps) {
|
|
|
348
348
|
</div>
|
|
349
349
|
</div>
|
|
350
350
|
|
|
351
|
-
<Modal show={showModal1} setShow={setShowModal1}
|
|
351
|
+
<Modal show={showModal1} setShow={setShowModal1}>
|
|
352
352
|
<h3 class="h3">Edit Profile</h3>
|
|
353
353
|
<p class="mb-5">An example modal containing a basic form for editing profiles.</p>
|
|
354
354
|
<form class="mb-8 text-left">
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import db from 'monastery'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'put /api/user/:uid': ['isUser', 'updateUser'],
|
|
8
|
-
},
|
|
9
|
-
update: update,
|
|
10
|
-
updateUser: updateUser,
|
|
4
|
+
export const routes = {
|
|
5
|
+
'put /api/company/:cid': ['isCompanyUser', update],
|
|
6
|
+
'put /api/user/:uid': ['isUser', updateUser],
|
|
11
7
|
}
|
|
12
8
|
|
|
13
9
|
async function update(req, res) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.64",
|
|
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 🚀",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@stripe/stripe-js": "^1.34.0",
|
|
58
|
-
"monastery": "~3.5.
|
|
58
|
+
"monastery": "~3.5.6",
|
|
59
59
|
"stripe": "^9.16.0"
|
|
60
60
|
},
|
|
61
61
|
"_peers-are-packages-that-will-be-used-in-the-host-repo-too": "",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!-- extends references the default nitro layout file -->
|
|
2
|
+
{% extends "partials/layout1.swig" %}
|
|
3
|
+
|
|
4
|
+
<!-- block content is the content of the email -->
|
|
5
|
+
{% block content %}
|
|
6
|
+
|
|
7
|
+
[[ subject = You've been invited to join IMG ]]
|
|
8
|
+
<b>%recipient.greet%</b>,<br/>
|
|
9
|
+
<br/>
|
|
10
|
+
You've been invited to join IMG.<br/>
|
|
11
|
+
<br/>
|
|
12
|
+
If you didn't expect to receive this email, feel free to disregard this email.<br/>
|
|
13
|
+
<br/>
|
|
14
|
+
<span mc:edit="button">
|
|
15
|
+
<a class="button" href="%recipient.domain%/invite/%recipient.token%" target="_blank">
|
|
16
|
+
Accept Invite
|
|
17
|
+
</a>
|
|
18
|
+
</span><br/>
|
|
19
|
+
<br/>
|
|
20
|
+
Thanks,<br/>
|
|
21
|
+
<b>The %recipient.configName% Team</b>
|
|
22
|
+
|
|
23
|
+
{% endblock %}
|
package/server/index.js
CHANGED
|
@@ -18,16 +18,12 @@ export { setupRouter } from './router.js'
|
|
|
18
18
|
// Export email utility
|
|
19
19
|
export { sendEmail } from './email/index.js'
|
|
20
20
|
|
|
21
|
-
// Export
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
getStore,
|
|
31
|
-
} from '../components/auth/auth.api.js'
|
|
32
|
-
export { default as settings } from '../components/settings/settings.api.js'
|
|
33
|
-
export { default as stripe } from '../components/billing/stripe.api.js'
|
|
21
|
+
// Export API controllers
|
|
22
|
+
export * from '../components/auth/auth.api.js'
|
|
23
|
+
export * from '../components/settings/settings.api.js'
|
|
24
|
+
export * from '../components/billing/stripe.api.js'
|
|
25
|
+
|
|
26
|
+
export { routes as authRoutes } from '../components/auth/auth.api.js'
|
|
27
|
+
export { routes as settingsRoutes } from '../components/settings/settings.api.js'
|
|
28
|
+
export { routes as stripeRoutes } from '../components/billing/stripe.api.js'
|
|
29
|
+
|
package/server/router.js
CHANGED
|
@@ -20,8 +20,9 @@ export async function setupRouter (config) {
|
|
|
20
20
|
const expressApp = express()
|
|
21
21
|
const server = http.createServer(expressApp)
|
|
22
22
|
const apiRoutes = {}
|
|
23
|
-
const controllers = {}
|
|
23
|
+
const controllers = {} // { controllerName: { ...routes, ...helpers } }
|
|
24
24
|
const allMiddleware = { ...defaultMiddleware, ...(middleware || {}) }
|
|
25
|
+
const verbs = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'all']
|
|
25
26
|
|
|
26
27
|
if (!env) {
|
|
27
28
|
throw new Error('setupRouter: `config.env` missing')
|
|
@@ -40,23 +41,27 @@ export async function setupRouter (config) {
|
|
|
40
41
|
let filepaths = getFiles(componentsDir, /\.api\.js$/)
|
|
41
42
|
// console.log(filepaths, componentsDir)
|
|
42
43
|
for (let filepath of filepaths) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const file = await import(filepath)
|
|
45
|
+
const routes = file.routes
|
|
46
|
+
const name = filepath.replace(/^.*[\\\/]|\.api\.js$/g, '') // eslint-disable-line
|
|
47
|
+
controllers[name] = routes // { ...routes, ...helpers }
|
|
46
48
|
|
|
47
|
-
if (!
|
|
48
|
-
console.warn(`API warning: no
|
|
49
|
+
if (!routes) {
|
|
50
|
+
console.warn(`API warning: no 'routes' export found on file ${filepath}`)
|
|
49
51
|
continue
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
54
|
-
util.each(
|
|
54
|
+
if (routes?.setup) routes.setup.call(routes, allMiddleware, config)
|
|
55
|
+
if (routes) {
|
|
56
|
+
util.each(routes, (_middleware, key) => {
|
|
57
|
+
if (!key.match(/\s/)) return
|
|
58
|
+
const match = key.match(new RegExp(`^(${verbs.join('|')})\\s+(.*)$`, 'i'))
|
|
59
|
+
if (!match) throw new Error(`Invalid verb or path: ${key}`)
|
|
55
60
|
apiRoutes[key] = {
|
|
56
61
|
middleware: util.toArray(_middleware),
|
|
57
62
|
filename: name,
|
|
58
|
-
path:
|
|
59
|
-
verb:
|
|
63
|
+
path: match[2],
|
|
64
|
+
verb: match[1],
|
|
60
65
|
}
|
|
61
66
|
})
|
|
62
67
|
}
|
|
@@ -265,39 +270,23 @@ function getFiles (dir, regexp) {
|
|
|
265
270
|
return paths
|
|
266
271
|
}
|
|
267
272
|
|
|
268
|
-
function resolveMiddleware (controllers, middleware, route, item
|
|
273
|
+
function resolveMiddleware (controllers, middleware, route, item) {
|
|
269
274
|
/**
|
|
270
275
|
* Resolves a placeholder string into a function
|
|
276
|
+
* @param {object} controllers - { controllerName: { routes, setup, ...other exported functions } }
|
|
277
|
+
* @param {object} middleware
|
|
271
278
|
* @param {object} route
|
|
272
279
|
* @param {fn|string} item
|
|
273
280
|
* @param {boolean} last - last item
|
|
274
281
|
* @return function(req, res){..}
|
|
275
282
|
*/
|
|
276
283
|
if (util.isFunction(item)) {
|
|
277
|
-
return item
|
|
284
|
+
return item.bind(controllers[route.filename])
|
|
278
285
|
|
|
279
|
-
} else if (
|
|
280
|
-
console.error('Invalid middleware item:', item)
|
|
286
|
+
} else if (typeof item !== 'string') {
|
|
287
|
+
console.error('Invalid middleware item on route:', route.path, item)
|
|
281
288
|
return
|
|
282
289
|
|
|
283
|
-
} else if (item.match(/\./) || last) { // e.g. user.read
|
|
284
|
-
let arr = item.split('.')
|
|
285
|
-
let controllerGroup = controllers[arr[1]? arr[0] : route.filename]
|
|
286
|
-
let controllerName = arr[1] || arr[0]
|
|
287
|
-
if (controllerGroup && controllerGroup[controllerName]) {
|
|
288
|
-
if (middleware.endpointSwitcher && controllerGroup[controllerName + 'Desktop']) {
|
|
289
|
-
return middleware.endpointSwitcher.bind(
|
|
290
|
-
null,
|
|
291
|
-
controllerGroup[controllerName].bind(controllerGroup),
|
|
292
|
-
controllerGroup[controllerName + 'Desktop'].bind(controllerGroup)
|
|
293
|
-
)
|
|
294
|
-
} else {
|
|
295
|
-
return controllerGroup[controllerName].bind(controllerGroup)
|
|
296
|
-
}
|
|
297
|
-
} else {
|
|
298
|
-
console.error(`The controller '${item}' defined in '${route.filename}.api' doesn't exist.`)
|
|
299
|
-
}
|
|
300
|
-
|
|
301
290
|
} else if (middleware[item]) {
|
|
302
291
|
return middleware[item]
|
|
303
292
|
|
package/types/util.d.ts
CHANGED
|
@@ -583,13 +583,14 @@ export function s3Image(awsUrl: string, imageOrArray: Image[] | Image, size?: st
|
|
|
583
583
|
export function sanitizeHTML(string: string): string;
|
|
584
584
|
/**
|
|
585
585
|
* Process scrollbar width once.
|
|
586
|
-
* @param {string} paddingClass - class name to give padding to
|
|
587
|
-
* @param {string} marginClass - class name to give margin to
|
|
588
|
-
* @param {number} maxWidth - enclose css in a max-width media query
|
|
586
|
+
* @param {string} [paddingClass] - class name to give padding to
|
|
587
|
+
* @param {string} [marginClass] - class name to give margin to
|
|
588
|
+
* @param {number} [maxWidth] - enclose css in a max-width media query
|
|
589
|
+
* @param {string} [marginClassNegative] - class name to give negative margin to
|
|
589
590
|
* @returns {number}
|
|
590
591
|
*
|
|
591
592
|
*/
|
|
592
|
-
export function scrollbar(paddingClass
|
|
593
|
+
export function scrollbar(paddingClass?: string, marginClass?: string, marginClassNegative?: string, maxWidth?: number): number;
|
|
593
594
|
/**
|
|
594
595
|
* Convert seconds to time
|
|
595
596
|
* @param {number} seconds
|
package/types/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
|
package/util.js
CHANGED
|
@@ -1410,13 +1410,14 @@ export function sanitizeHTML (string) {
|
|
|
1410
1410
|
|
|
1411
1411
|
/**
|
|
1412
1412
|
* Process scrollbar width once.
|
|
1413
|
-
* @param {string} paddingClass - class name to give padding to
|
|
1414
|
-
* @param {string} marginClass - class name to give margin to
|
|
1415
|
-
* @param {number} maxWidth - enclose css in a max-width media query
|
|
1413
|
+
* @param {string} [paddingClass] - class name to give padding to
|
|
1414
|
+
* @param {string} [marginClass] - class name to give margin to
|
|
1415
|
+
* @param {number} [maxWidth] - enclose css in a max-width media query
|
|
1416
|
+
* @param {string} [marginClassNegative] - class name to give negative margin to
|
|
1416
1417
|
* @returns {number}
|
|
1417
1418
|
*
|
|
1418
1419
|
*/
|
|
1419
|
-
export function scrollbar (paddingClass, marginClass, maxWidth) {
|
|
1420
|
+
export function scrollbar (paddingClass, marginClass, marginClassNegative, maxWidth) {
|
|
1420
1421
|
if (typeof window === 'undefined') return 0
|
|
1421
1422
|
if (scrollbarCache || scrollbarCache === 0) {
|
|
1422
1423
|
return scrollbarCache
|
|
@@ -1452,6 +1453,7 @@ export function scrollbar (paddingClass, marginClass, maxWidth) {
|
|
|
1452
1453
|
(maxWidth ? '@media only screen and (max-width: ' + maxWidth + 'px) {' : '') +
|
|
1453
1454
|
(paddingClass ? paddingClass + ' {padding-right:' + scrollbarCache + 'px}' : '') +
|
|
1454
1455
|
(marginClass ? marginClass + ' {margin-right:' + scrollbarCache + 'px}' : '') +
|
|
1456
|
+
(marginClassNegative ? marginClassNegative + ' {margin-right:' + scrollbarCache * -1 + 'px}' : '') +
|
|
1455
1457
|
(maxWidth ? '}' : '')
|
|
1456
1458
|
document.head.appendChild(style)
|
|
1457
1459
|
}
|