nitro-web 0.0.25 → 0.0.27
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 +26 -7
- package/client/index.ts +2 -2
- package/client/store.ts +0 -1
- package/components/auth/auth.api.js +63 -126
- package/components/auth/signin.tsx +8 -6
- package/components/auth/signup.tsx +7 -7
- package/components/partials/element/sidebar.tsx +2 -2
- package/components/partials/styleguide.tsx +2 -2
- package/package.json +5 -4
- package/types/required-globals.d.ts +0 -1
- package/types.ts +13 -8
package/client/app.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { AxiosRequestConfig } from '@hokify/axios'
|
|
|
5
5
|
import { beforeCreate, Provider, exposedData } from './store'
|
|
6
6
|
import { axios, camelCase, pick, toArray, setTimeoutPromise } from 'nitro-web/util'
|
|
7
7
|
import { Config, Store } from 'nitro-web/types'
|
|
8
|
+
import { injectedConfig } from './index'
|
|
8
9
|
|
|
9
10
|
type LayoutProps = {
|
|
10
11
|
config: Config;
|
|
@@ -22,7 +23,7 @@ type Settings = {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
type Route = {
|
|
25
|
-
component: React.FC<{ route?: Route; params?: object; location?: object }>
|
|
26
|
+
component: React.FC<{ route?: Route; params?: object; location?: object; config?: Config }>
|
|
26
27
|
meta?: { title?: string }
|
|
27
28
|
middleware: string[]
|
|
28
29
|
name: string
|
|
@@ -41,6 +42,10 @@ export async function setupApp(config: Config, layouts: React.FC<LayoutProps>[])
|
|
|
41
42
|
name: config.name,
|
|
42
43
|
titleSeparator: config.titleSeparator,
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
// Setup the jwt token
|
|
47
|
+
updateJwt(localStorage.getItem(injectedConfig.jwtName))
|
|
48
|
+
|
|
44
49
|
if (!settings.layouts) throw new Error('layouts are required')
|
|
45
50
|
const initData = (await settings.beforeApp(config)) || {}
|
|
46
51
|
beforeCreate(initData, settings.beforeStoreUpdate)
|
|
@@ -49,6 +54,14 @@ export async function setupApp(config: Config, layouts: React.FC<LayoutProps>[])
|
|
|
49
54
|
root.render(<App settings={settings} config={config} />)
|
|
50
55
|
}
|
|
51
56
|
|
|
57
|
+
export function updateJwt(token?: string | null) {
|
|
58
|
+
// Update the jwt token in local storage and axios headers
|
|
59
|
+
const key = injectedConfig.jwtName
|
|
60
|
+
localStorage.setItem(key, token || '')
|
|
61
|
+
if (token) axios().defaults.headers.Authorization = `Bearer ${token}`
|
|
62
|
+
else delete axios().defaults.headers.Authorization
|
|
63
|
+
}
|
|
64
|
+
|
|
52
65
|
function App({ settings, config }: { settings: Settings, config: Config }): ReactNode {
|
|
53
66
|
// const themeNormalised = theme
|
|
54
67
|
const router = getRouter({ settings, config })
|
|
@@ -182,7 +195,7 @@ function getRouter({ settings, config }: { settings: Settings, config: Config })
|
|
|
182
195
|
children: layout.map((route) => {
|
|
183
196
|
return {
|
|
184
197
|
element: (
|
|
185
|
-
<RouteComponent route={route} />
|
|
198
|
+
<RouteComponent route={route} config={config} />
|
|
186
199
|
),
|
|
187
200
|
path: route.path,
|
|
188
201
|
loader: async () => { // request
|
|
@@ -230,13 +243,13 @@ function RestoreScroll() {
|
|
|
230
243
|
return (null)
|
|
231
244
|
}
|
|
232
245
|
|
|
233
|
-
function RouteComponent({ route }: { route: Route }) {
|
|
246
|
+
function RouteComponent({ route, config }: { route: Route, config: Config }) {
|
|
234
247
|
const Component = route.component
|
|
235
248
|
const params = useParams()
|
|
236
249
|
const location = useLocation()
|
|
237
250
|
document.title = route.meta?.title || ''
|
|
238
251
|
return (
|
|
239
|
-
<Component route={route} params={params} location={location} />
|
|
252
|
+
<Component route={route} params={params} location={location} config={config} />
|
|
240
253
|
)
|
|
241
254
|
}
|
|
242
255
|
|
|
@@ -280,6 +293,13 @@ function beforeStoreUpdate(prevStore: Store | null, newData: Store) {
|
|
|
280
293
|
* @return {object} store
|
|
281
294
|
*/
|
|
282
295
|
if (!newData) return newData
|
|
296
|
+
|
|
297
|
+
// If newData.jwt is present, update the jwt token
|
|
298
|
+
if (newData.jwt) {
|
|
299
|
+
updateJwt(newData.jwt)
|
|
300
|
+
delete newData.jwt
|
|
301
|
+
}
|
|
302
|
+
|
|
283
303
|
const store = {
|
|
284
304
|
...(prevStore || {
|
|
285
305
|
message: undefined,
|
|
@@ -288,9 +308,8 @@ function beforeStoreUpdate(prevStore: Store | null, newData: Store) {
|
|
|
288
308
|
...(newData || {}),
|
|
289
309
|
}
|
|
290
310
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
axios().defaults.headers.userid = store?.user?._id
|
|
311
|
+
// E.g. Cookie matching handy for rare issues, e.g. signout > signin (to a different user on another tab)
|
|
312
|
+
axios().defaults.headers.authid = store?.user?._id
|
|
294
313
|
return store
|
|
295
314
|
}
|
|
296
315
|
|
package/client/index.ts
CHANGED
|
@@ -48,5 +48,5 @@ export { Toggle } from '../components/partials/form/toggle'
|
|
|
48
48
|
// Component Other
|
|
49
49
|
export { IsFirstRender } from '../components/partials/is-first-render'
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
export const
|
|
51
|
+
// Expose the injected config
|
|
52
|
+
export const injectedConfig = { ...INJECTED_CONFIG } as import('types').Config
|
package/client/store.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import MongoStore from 'connect-mongo'
|
|
3
2
|
import crypto from 'crypto'
|
|
4
|
-
import expressSession from 'express-session'
|
|
5
3
|
import passport from 'passport'
|
|
6
4
|
import passportLocal from 'passport-local'
|
|
5
|
+
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'
|
|
7
6
|
import db from 'monastery'
|
|
7
|
+
import jsonwebtoken from 'jsonwebtoken'
|
|
8
8
|
import { sendEmail } from 'nitro-web/server'
|
|
9
9
|
import * as util from 'nitro-web/util'
|
|
10
|
-
// import stripeController from '../billing/stripe.api.js'
|
|
11
10
|
|
|
12
11
|
let config = {}
|
|
12
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'replace_this_with_secure_env_secret'
|
|
13
13
|
|
|
14
14
|
export default {
|
|
15
15
|
|
|
@@ -24,43 +24,19 @@ export default {
|
|
|
24
24
|
},
|
|
25
25
|
|
|
26
26
|
setup: function (middleware, _config) {
|
|
27
|
-
// Setup passport handlers for reading and writing to req.session
|
|
28
27
|
const that = this
|
|
29
28
|
global.passport = passport
|
|
30
29
|
|
|
31
30
|
// Set config values
|
|
32
|
-
config = {
|
|
33
|
-
|
|
34
|
-
masterPassword: _config.masterPassword,
|
|
35
|
-
}
|
|
36
|
-
for (let key in config) {
|
|
37
|
-
if (!config[key] && key != 'masterPassword') {
|
|
38
|
-
throw new Error(`Missing config value for stripe.api.js: ${key}`)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// After successful login, serialize the user into a session object
|
|
43
|
-
passport.serializeUser((user, next) => {
|
|
44
|
-
next(null, { _id: user._id })
|
|
45
|
-
})
|
|
31
|
+
config = { env: _config.env, masterPassword: _config.masterPassword }
|
|
32
|
+
if (!config.env) throw new Error('Missing config value for: config.env')
|
|
46
33
|
|
|
47
|
-
// After session read, get the user from the session object
|
|
48
|
-
passport.deserializeUser(async (sessionObject, next) => {
|
|
49
|
-
try {
|
|
50
|
-
const user = await that._findUserFromProvider('deserialize', sessionObject)
|
|
51
|
-
next(null, user)
|
|
52
|
-
} catch (err) {
|
|
53
|
-
next(err.message)
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
// Setup passport local signin strategy
|
|
58
34
|
passport.use(
|
|
59
35
|
new passportLocal.Strategy(
|
|
60
|
-
{ usernameField: 'email' },
|
|
36
|
+
{ usernameField: 'email' },
|
|
61
37
|
async (email, password, next) => {
|
|
62
38
|
try {
|
|
63
|
-
const user = await that._findUserFromProvider('email', { email
|
|
39
|
+
const user = await that._findUserFromProvider('email', { email, password })
|
|
64
40
|
next(null, user)
|
|
65
41
|
} catch (err) {
|
|
66
42
|
next(err.message)
|
|
@@ -69,58 +45,45 @@ export default {
|
|
|
69
45
|
)
|
|
70
46
|
)
|
|
71
47
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// });
|
|
93
|
-
// }));
|
|
94
|
-
|
|
95
|
-
// Add session middleware
|
|
96
|
-
middleware.order.splice(3, 0, 'session', 'passport', 'passportSession', 'passportError', 'blocked')
|
|
48
|
+
passport.use(
|
|
49
|
+
new JwtStrategy(
|
|
50
|
+
{
|
|
51
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
52
|
+
secretOrKey: JWT_SECRET,
|
|
53
|
+
},
|
|
54
|
+
async (payload, done) => {
|
|
55
|
+
try {
|
|
56
|
+
const user = await that._findUserFromProvider('deserialize', { _id: payload._id })
|
|
57
|
+
if (!user) return done(null, false)
|
|
58
|
+
return done(null, user)
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return done(err, false)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
middleware.order.splice(3, 0, 'passport', 'passportError', 'jwtAuth', 'blocked')
|
|
67
|
+
|
|
97
68
|
Object.assign(middleware, {
|
|
98
69
|
blocked: function (req, res, next) {
|
|
99
70
|
if (req.user && req.user.loginActive === false) {
|
|
100
|
-
|
|
101
|
-
res.error('This user is not available.')
|
|
71
|
+
res.status(403).error('This user is not available.')
|
|
102
72
|
} else {
|
|
103
73
|
next()
|
|
104
74
|
}
|
|
105
75
|
},
|
|
76
|
+
jwtAuth: function(req, res, next) {
|
|
77
|
+
passport.authenticate('jwt', { session: false }, function(err, user) {
|
|
78
|
+
if (user) req.user = user
|
|
79
|
+
next()
|
|
80
|
+
})(req, res, next)
|
|
81
|
+
},
|
|
106
82
|
passport: passport.initialize(),
|
|
107
83
|
passportError: function (err, req, res, next) {
|
|
108
84
|
if (!err) return next()
|
|
109
|
-
req.logout()
|
|
110
85
|
res.error(err)
|
|
111
86
|
},
|
|
112
|
-
passportSession: passport.session(),
|
|
113
|
-
session: expressSession({
|
|
114
|
-
secret: '092720e5ffc1237266b8517239cd81b6', // Changing invalidates cookies
|
|
115
|
-
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
|
|
116
|
-
resave: false,
|
|
117
|
-
saveUninitialized: false,
|
|
118
|
-
store: MongoStore.create({
|
|
119
|
-
clientPromise: db.onOpen((manager) => {
|
|
120
|
-
return manager.client
|
|
121
|
-
}),
|
|
122
|
-
}),
|
|
123
|
-
}),
|
|
124
87
|
})
|
|
125
88
|
},
|
|
126
89
|
|
|
@@ -131,31 +94,26 @@ export default {
|
|
|
131
94
|
signup: async function (req, res) {
|
|
132
95
|
try {
|
|
133
96
|
let user = await this._userCreate(req.body)
|
|
134
|
-
// Welcome email
|
|
135
97
|
sendEmail({
|
|
136
98
|
config: config,
|
|
137
99
|
template: 'welcome',
|
|
138
100
|
to: `${util.ucFirst(user.firstName)}<${user.email}>`,
|
|
139
|
-
}).catch(
|
|
140
|
-
|
|
141
|
-
})
|
|
142
|
-
// Login
|
|
143
|
-
res.send(await this._signinAndGetState(req, user))
|
|
101
|
+
}).catch(console.error)
|
|
102
|
+
res.send(await this._signinAndGetState(user, req.query.desktop))
|
|
144
103
|
} catch (err) {
|
|
145
104
|
res.error(err)
|
|
146
105
|
}
|
|
147
106
|
},
|
|
148
107
|
|
|
149
108
|
signin: function (req, res) {
|
|
150
|
-
// console.log('api: signin')
|
|
151
|
-
// console.log(req.body)
|
|
152
109
|
if (!req.body.email) return res.error('email', 'The email you entered is incorrect.')
|
|
153
110
|
if (!req.body.password) return res.error('password', 'The password you entered is incorrect.')
|
|
111
|
+
|
|
154
112
|
passport.authenticate('local', { session: false }, async (err, user, info) => {
|
|
155
|
-
if (err) return
|
|
113
|
+
if (err) return res.error(err)
|
|
156
114
|
if (!user && info) return res.error('email', info.message)
|
|
157
115
|
try {
|
|
158
|
-
const response = await this._signinAndGetState(
|
|
116
|
+
const response = await this._signinAndGetState(user, req.query.desktop)
|
|
159
117
|
res.send(response)
|
|
160
118
|
} catch (err) {
|
|
161
119
|
res.error(err)
|
|
@@ -164,35 +122,29 @@ export default {
|
|
|
164
122
|
},
|
|
165
123
|
|
|
166
124
|
signout: function (req, res) {
|
|
167
|
-
req.logout()
|
|
168
125
|
res.json('{}')
|
|
169
126
|
},
|
|
170
127
|
|
|
171
128
|
resetInstructions: async function (req, res) {
|
|
172
129
|
try {
|
|
173
|
-
let email = (req.body.email||'').trim().toLowerCase()
|
|
174
|
-
if (!email || !util.isString(email)) {
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
// Find matching user and create new reset token
|
|
178
|
-
let user = await db.user.findOne({ query: { email: email }, _privateData: true })
|
|
130
|
+
let email = (req.body.email || '').trim().toLowerCase()
|
|
131
|
+
if (!email || !util.isString(email)) throw { title: 'email', detail: 'The email you entered is incorrect.' }
|
|
132
|
+
|
|
133
|
+
let user = await db.user.findOne({ query: { email }, _privateData: true })
|
|
179
134
|
if (!user) throw { title: 'email', detail: 'The email you entered is incorrect.' }
|
|
180
|
-
|
|
181
|
-
let
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// Email.
|
|
135
|
+
|
|
136
|
+
let resetToken = await this._tokenCreate(user._id)
|
|
137
|
+
await db.user.update({ query: { email }, $set: { resetToken }})
|
|
138
|
+
|
|
185
139
|
res.json({})
|
|
186
140
|
sendEmail({
|
|
187
141
|
config: config,
|
|
188
142
|
template: 'reset-password',
|
|
189
143
|
to: `${util.ucFirst(user.firstName)}<${email}>`,
|
|
190
144
|
data: {
|
|
191
|
-
token:
|
|
145
|
+
token: resetToken + (req.query.hasOwnProperty('desktop') ? '?desktop' : ''),
|
|
192
146
|
},
|
|
193
|
-
}).catch(err =>
|
|
194
|
-
console.error('sendEmail(..) mailgun error', err)
|
|
195
|
-
})
|
|
147
|
+
}).catch(err => console.error('sendEmail(..) mailgun error', err))
|
|
196
148
|
} catch (err) {
|
|
197
149
|
res.error(err)
|
|
198
150
|
}
|
|
@@ -202,18 +154,11 @@ export default {
|
|
|
202
154
|
try {
|
|
203
155
|
const { token, password, password2 } = req.body
|
|
204
156
|
const id = this._tokenParse(token)
|
|
205
|
-
// Validate password
|
|
206
157
|
this._validatePassword(password, password2)
|
|
207
|
-
|
|
208
|
-
let user = await db.user.findOne({
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
_privateData: true,
|
|
212
|
-
})
|
|
213
|
-
if (!user || user.resetToken !== token) {
|
|
214
|
-
throw new Error('Sorry your email token is invalid or has already been used verify your email.')
|
|
215
|
-
}
|
|
216
|
-
// Update user with new password
|
|
158
|
+
|
|
159
|
+
let user = await db.user.findOne({ query: id, blacklist: ['-resetToken'], _privateData: true })
|
|
160
|
+
if (!user || user.resetToken !== token) throw new Error('Sorry your email token is invalid or has already been used.')
|
|
161
|
+
|
|
217
162
|
await db.user.update({
|
|
218
163
|
query: user._id,
|
|
219
164
|
data: {
|
|
@@ -222,7 +167,7 @@ export default {
|
|
|
222
167
|
},
|
|
223
168
|
blacklist: ['-resetToken', '-password'],
|
|
224
169
|
})
|
|
225
|
-
res.send(await this._signinAndGetState(
|
|
170
|
+
res.send(await this._signinAndGetState({ ...user, resetToken: undefined }, req.query.desktop))
|
|
226
171
|
} catch (err) {
|
|
227
172
|
res.error(err)
|
|
228
173
|
}
|
|
@@ -264,27 +209,19 @@ export default {
|
|
|
264
209
|
/* ---- Private fns ---------------- */
|
|
265
210
|
|
|
266
211
|
_getState: async function (user) {
|
|
267
|
-
//
|
|
268
|
-
return {
|
|
212
|
+
// Initial state
|
|
213
|
+
return {
|
|
269
214
|
user: user || null,
|
|
270
|
-
// stripeProducts: await stripeController._getProducts(),
|
|
271
215
|
}
|
|
272
216
|
},
|
|
273
217
|
|
|
274
|
-
_signinAndGetState: function (
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
resolve(await this._getState(user))
|
|
282
|
-
})
|
|
283
|
-
} else {
|
|
284
|
-
return reject('This user is not available.')
|
|
285
|
-
// this._getState().then((state) => resolve(state))
|
|
286
|
-
}
|
|
287
|
-
})
|
|
218
|
+
_signinAndGetState: async function (user, isDesktop) {
|
|
219
|
+
if (user.loginActive === false) throw 'This user is not available.'
|
|
220
|
+
user.desktop = isDesktop
|
|
221
|
+
|
|
222
|
+
const jwt = jsonwebtoken.sign({ _id: user._id }, JWT_SECRET, { expiresIn: '30d' })
|
|
223
|
+
const state = await this._getState(user)
|
|
224
|
+
return { ...state, jwt }
|
|
288
225
|
},
|
|
289
226
|
|
|
290
227
|
_tokenCreate: function (id) {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Topbar, Field, Button, FormError, util } from 'nitro-web'
|
|
2
|
-
import {
|
|
1
|
+
import { Topbar, Field, Button, FormError, util, injectedConfig, updateJwt } from 'nitro-web'
|
|
2
|
+
import { Errors } from 'nitro-web/types'
|
|
3
3
|
|
|
4
|
-
export function Signin(
|
|
4
|
+
export function Signin() {
|
|
5
5
|
const navigate = useNavigate()
|
|
6
6
|
const location = useLocation()
|
|
7
7
|
const isSignout = location.pathname == '/signout'
|
|
8
8
|
const isLoading = useState(isSignout ? 'is-loading' : '')
|
|
9
9
|
const [, setStore] = useTracked()
|
|
10
10
|
const [state, setState] = useState({
|
|
11
|
-
email:
|
|
12
|
-
password:
|
|
11
|
+
email: injectedConfig.env == 'development' ? (injectedConfig.placeholderEmail || '') : '',
|
|
12
|
+
password: injectedConfig.env == 'development' ? '1234' : '',
|
|
13
13
|
errors: [] as Errors,
|
|
14
14
|
})
|
|
15
15
|
|
|
@@ -22,8 +22,10 @@ export function Signin({ config }: { config: Config }) {
|
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
if (isSignout) {
|
|
24
24
|
setStore(() => ({ user: null }))
|
|
25
|
-
util.axios().get('/api/signout')
|
|
25
|
+
// util.axios().get('/api/signout')
|
|
26
|
+
Promise.resolve()
|
|
26
27
|
.then(() => isLoading[1](''))
|
|
28
|
+
.then(() => updateJwt())
|
|
27
29
|
.then(() => navigate({ pathname: '/signin', search: location.search }, { replace: true }))
|
|
28
30
|
.catch(err => (console.error(err), isLoading[1]('')))
|
|
29
31
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Button, Field, FormError, Topbar, util } from 'nitro-web'
|
|
2
|
-
import {
|
|
1
|
+
import { Button, Field, FormError, Topbar, util, injectedConfig } from 'nitro-web'
|
|
2
|
+
import { Errors } from 'nitro-web/types'
|
|
3
3
|
|
|
4
|
-
export function Signup(
|
|
4
|
+
export function Signup() {
|
|
5
5
|
const navigate = useNavigate()
|
|
6
6
|
const isLoading = useState('')
|
|
7
7
|
const [, setStore] = useTracked()
|
|
8
8
|
const [state, setState] = useState({
|
|
9
|
-
email:
|
|
10
|
-
name:
|
|
11
|
-
business: { name:
|
|
12
|
-
password:
|
|
9
|
+
email: injectedConfig.env === 'development' ? (injectedConfig.placeholderEmail || '') : '',
|
|
10
|
+
name: injectedConfig.env === 'development' ? 'Bruce Wayne' : '',
|
|
11
|
+
business: { name: injectedConfig.env === 'development' ? 'Wayne Enterprises' : '' },
|
|
12
|
+
password: injectedConfig.env === 'development' ? '1234' : '',
|
|
13
13
|
errors: [] as Errors,
|
|
14
14
|
})
|
|
15
15
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Component: https://tailwindui.com/components/application-ui/application-shells/sidebar#component-a69d85b6237ea2ad506c00ef1cd39a38
|
|
2
2
|
import { Dialog, DialogBackdrop, DialogPanel, TransitionChild } from '@headlessui/react'
|
|
3
3
|
import avatarImg from 'nitro-web/client/imgs/avatar.jpg'
|
|
4
|
-
import {
|
|
4
|
+
import { injectedConfig } from 'nitro-web'
|
|
5
5
|
import {
|
|
6
6
|
Bars3Icon,
|
|
7
7
|
HomeIcon,
|
|
@@ -85,7 +85,7 @@ function SidebarContents ({ Logo, menu, links, version }: SidebarProps) {
|
|
|
85
85
|
|
|
86
86
|
const _menu = menu || [
|
|
87
87
|
{ name: 'Dashboard', to: '/', Icon: HomeIcon },
|
|
88
|
-
{ name: isDemo ? 'Design System' : 'Style Guide', to: '/styleguide', Icon: PaintBrushIcon },
|
|
88
|
+
{ name: injectedConfig.isDemo ? 'Design System' : 'Style Guide', to: '/styleguide', Icon: PaintBrushIcon },
|
|
89
89
|
{ name: 'Pricing', to: '/pricing', Icon: UsersIcon },
|
|
90
90
|
{ name: 'Signout', to: '/signout', Icon: ArrowLeftCircleIcon },
|
|
91
91
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink,
|
|
1
|
+
import { Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink, Modal, Calendar, injectedConfig } from 'nitro-web'
|
|
2
2
|
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
3
3
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
|
4
4
|
import { Config } from 'nitro-web/types'
|
|
@@ -55,7 +55,7 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
55
55
|
<div class="mb-10 text-left max-w-[1100px]">
|
|
56
56
|
<GithubLink filename={__filename} />
|
|
57
57
|
<div class="mb-7">
|
|
58
|
-
<h1 class="h1">{isDemo ? 'Design System' : 'Style Guide'}</h1>
|
|
58
|
+
<h1 class="h1">{injectedConfig.isDemo ? 'Design System' : 'Style Guide'}</h1>
|
|
59
59
|
<p>
|
|
60
60
|
Components are styled using
|
|
61
61
|
<a href="https://v3.tailwindcss.com/docs/configuration" class="underline" target="_blank" rel="noreferrer">TailwindCSS</a>.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
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 🚀",
|
|
@@ -33,19 +33,20 @@
|
|
|
33
33
|
"bcrypt": "^5.0.0",
|
|
34
34
|
"body-parser": "^1.19.0",
|
|
35
35
|
"compression": "^1.7.4",
|
|
36
|
-
"connect-mongo": "^5.1.0",
|
|
37
36
|
"date-fns": "^3.6.0",
|
|
38
37
|
"dateformat": "^3.0.3",
|
|
39
38
|
"dotenv": "^14.3.2",
|
|
40
39
|
"express": "^4.17.1",
|
|
41
40
|
"express-fileupload": "^1.1.6",
|
|
42
|
-
"express-session": "^1.17.0",
|
|
43
41
|
"inline-css": "^4.0.2",
|
|
42
|
+
"jsonwebtoken": "^9.0.2",
|
|
44
43
|
"nodemailer": "^6.5.0",
|
|
45
44
|
"nodemailer-mailgun-transport": "^2.0.2",
|
|
46
45
|
"nunjucks": "^3.2.2",
|
|
47
46
|
"passport": "^0.4.1",
|
|
48
|
-
"passport-
|
|
47
|
+
"passport-jwt": "^4.0.1",
|
|
48
|
+
"passport-local": "^1.0.0",
|
|
49
|
+
"sort-route-addresses-nodeps": "0.0.4"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
52
|
"@stripe/stripe-js": "^1.34.0",
|
|
@@ -7,7 +7,6 @@ import { CSSInterpolation } from '@emotion/serialize'
|
|
|
7
7
|
declare global {
|
|
8
8
|
/** Webpack injected config variables */
|
|
9
9
|
const INJECTED_CONFIG: Record<string, string|boolean|object>
|
|
10
|
-
const ISDEMO: boolean
|
|
11
10
|
/** Webpack svg loader */
|
|
12
11
|
module '*.svg' {
|
|
13
12
|
const content: React.FC<React.SVGProps<SVGElement>>
|
package/types.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
type InjectedConfig = {
|
|
2
|
+
awsUrl?: string
|
|
3
3
|
clientUrl: string
|
|
4
4
|
countries: { [key: string]: { numberFormats: { currency: string } } } // for input-currency.tsx
|
|
5
5
|
currencies: { [key: string]: { symbol: string, digits: number } } // for input-currency.tsx
|
|
6
6
|
env: string
|
|
7
|
+
googleMapsApiKey?: string
|
|
8
|
+
isDemo: boolean // implicitly defined by webpack
|
|
9
|
+
isStatic: boolean // implicitly defined by webpack
|
|
10
|
+
jwtName: string // implicitly defined by webpack
|
|
7
11
|
name: string
|
|
12
|
+
placeholderEmail?: string
|
|
13
|
+
stripePublishableKey?: string
|
|
14
|
+
titleSeparator?: string
|
|
8
15
|
version: string
|
|
16
|
+
}
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
export type Config = InjectedConfig & {
|
|
19
|
+
// Non-injectable config on the client
|
|
11
20
|
beforeApp?: () => Promise<object>
|
|
12
21
|
beforeStoreUpdate?: (prevStore: Store | null, newData: Store) => Store
|
|
13
|
-
googleMapsApiKey?: string
|
|
14
|
-
isStatic?: boolean
|
|
15
22
|
middleware?: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
|
|
16
|
-
placeholderEmail?: string
|
|
17
|
-
stripePublishableKey?: string
|
|
18
|
-
titleSeparator?: string
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export type User = {
|
|
@@ -45,6 +49,7 @@ export type Store = {
|
|
|
45
49
|
message?: MessageObject | string | null
|
|
46
50
|
user?: User | null,
|
|
47
51
|
apiAvailable?: boolean
|
|
52
|
+
jwt?: string
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
export type Svg = React.FC<React.SVGProps<SVGElement>>
|