nitro-web 0.0.165 → 0.0.167
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 +4 -8
- package/client/store.ts +2 -2
- package/components/partials/not-found.tsx +6 -2
- package/package.json +1 -1
- package/server/index.js +1 -1
- package/server/router.js +47 -31
- package/types/server/index.d.ts +1 -1
- package/types/server/router.d.ts +2 -0
- package/types/server/router.d.ts.map +1 -1
package/client/app.tsx
CHANGED
|
@@ -219,14 +219,10 @@ function getRouter({ settings, config }: { settings: Settings, config: Config })
|
|
|
219
219
|
}
|
|
220
220
|
for (const key of route.middleware) {
|
|
221
221
|
const error = settings.middleware[key](route, exposedStoreData || {})
|
|
222
|
+
// Note: the redirect uses the new pathname for query string values, e.g. '?example=value'. We also can't use the
|
|
223
|
+
// current pathname, as this doesn't exist on page refresh.
|
|
222
224
|
if (error && error.redirect) {
|
|
223
|
-
|
|
224
|
-
// redirect loops! We assume these query string redirects are intended to be relative to the current path.
|
|
225
|
-
if (error.redirect.startsWith('?')) {
|
|
226
|
-
return redirect(window.location.pathname + error.redirect)
|
|
227
|
-
} else {
|
|
228
|
-
return redirect(error.redirect)
|
|
229
|
-
}
|
|
225
|
+
return redirect(error.redirect)
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
228
|
return null
|
|
@@ -286,7 +282,7 @@ function catchRedirectLoop(request: Request, routeName: string) {
|
|
|
286
282
|
lastRedirectTimesForSameUrl.push(Date.now())
|
|
287
283
|
if (lastRedirectTimesForSameUrl.length > 5 && (Date.now() < lastRedirectTime + 100)) {
|
|
288
284
|
throw new Error(
|
|
289
|
-
`Nitro: A redirect loop has been detected for route '${routeName}'. This
|
|
285
|
+
`Nitro: A redirect loop has been detected for route '${routeName}'. This is most likely due to a redirect loop caused by your middleware. Consider triggered by redirect values without a pathname, e.g. '?example=value'.`
|
|
290
286
|
)
|
|
291
287
|
}
|
|
292
288
|
} else {
|
package/client/store.ts
CHANGED
|
@@ -39,9 +39,9 @@ function beforeUpdate<T extends Store>(newStore: T) {
|
|
|
39
39
|
delete newStore.jwt
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
//
|
|
42
|
+
// Send the requesting user id in the headers for the server to check (see, ./server/router.js:isValidUser() for more details)
|
|
43
43
|
if (newStore?.user?._id) {
|
|
44
|
-
axios().defaults.headers.
|
|
44
|
+
axios().defaults.headers.requestingUserId = newStore?.user?._id
|
|
45
45
|
}
|
|
46
46
|
return newStore
|
|
47
47
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export function NotFound() {
|
|
2
2
|
return (
|
|
3
|
-
<div
|
|
4
|
-
|
|
3
|
+
<div style={{'minHeight': '300px'}}>
|
|
4
|
+
<span class="h1">Page Not Found</span><br />
|
|
5
|
+
<br />
|
|
6
|
+
The page you're looking for doesn't exist or has moved.<br />
|
|
7
|
+
<br />
|
|
8
|
+
<Link to="/">Go back home</Link> or check the URL and try again.
|
|
5
9
|
</div>
|
|
6
10
|
)
|
|
7
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.167",
|
|
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 🚀",
|
package/server/index.js
CHANGED
|
@@ -18,7 +18,7 @@ async function setupDefaultModels(db) {
|
|
|
18
18
|
export { userModel, companyModel, setupDefaultModels }
|
|
19
19
|
|
|
20
20
|
// Export router
|
|
21
|
-
export { setupRouter, middleware } from './router.js'
|
|
21
|
+
export { setupRouter, middleware, isValidUserOrRespond, isAdminUser } from './router.js'
|
|
22
22
|
|
|
23
23
|
// Export email utility
|
|
24
24
|
export { sendEmail } from './email/index.js'
|
package/server/router.js
CHANGED
|
@@ -374,42 +374,58 @@ export const middleware = {
|
|
|
374
374
|
|
|
375
375
|
|
|
376
376
|
isAdmin: (req, res, next) => {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
let cookieMatch = user && (!req.headers.authid || user._id?.toString() == req.headers.authid)
|
|
381
|
-
if (cookieMatch && user && (user.type?.match(/admin/) || user.isAdmin)) next()
|
|
382
|
-
else if (user && (user.type?.match(/admin/) || user.isAdmin)) res.unauthorized('Invalid cookie, please refresh your browser')
|
|
383
|
-
else if (user) res.unauthorized('You are not authorised to make this request.')
|
|
384
|
-
else res.unauthorized('Please sign in first.')
|
|
377
|
+
if (!isValidUserOrRespond(req, res)) return
|
|
378
|
+
else if (!isAdminUser(req)) res.unauthorized('You are not authorised to make this request.')
|
|
379
|
+
else next()
|
|
385
380
|
},
|
|
386
|
-
// isCompanyOwner: (req, res, next) => {
|
|
387
|
-
// let user = req.user || { companies: [] }
|
|
388
|
-
// let cid = req.params.cid
|
|
389
|
-
// let company = user.companies.find((o) => o._id.toString() == cid)
|
|
390
|
-
// let companyUser = company?.users?.find((o) => o._id.toString() == user._id?.toString())
|
|
391
|
-
// if (!user._id) return res.unauthorized('Please sign in first.')
|
|
392
|
-
// else if (!company || !companyUser) res.unauthorized('You are not authorised to make this request.')
|
|
393
|
-
// else if (companyUser.type != 'owner') res.unauthorized('Only owners can make this request.')
|
|
394
|
-
// else next()
|
|
395
|
-
// },
|
|
396
|
-
// isCompanyUser: (req, res, next) => {
|
|
397
|
-
// let user = req.user || { companies: [] }
|
|
398
|
-
// let cid = req.params.cid
|
|
399
|
-
// let company = user.companies.find((o) => o._id.toString() == cid)
|
|
400
|
-
// if (!user._id) return res.unauthorized('Please sign in first.')
|
|
401
|
-
// else if (!company) res.unauthorized('You are not authorised to make this request.')
|
|
402
|
-
// else next()
|
|
403
|
-
// },
|
|
404
381
|
isUser: (req, res, next) => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
382
|
+
if (!isValidUserOrRespond(req, res)) return
|
|
383
|
+
else next()
|
|
384
|
+
},
|
|
385
|
+
isParamUser: (req, res, next) => {
|
|
386
|
+
const isParamUser = req.user?._id?.toString() == req.params.uid
|
|
387
|
+
if (!isValidUserOrRespond(req, res)) return
|
|
388
|
+
else if (!isParamUser && !isAdminUser(req)) res.unauthorized('You are not authorised to make this request.')
|
|
389
|
+
else next()
|
|
410
390
|
},
|
|
411
391
|
isDevelopment: (req, res, next) => {
|
|
412
392
|
if (configLocal.env !== 'development') res.error('This API endpoint is only available in development')
|
|
413
393
|
else next()
|
|
414
394
|
},
|
|
395
|
+
isCompanyUser: (req, res, next) => {
|
|
396
|
+
if (!isValidParamCompanyUserOrRespond(req)) return
|
|
397
|
+
else next()
|
|
398
|
+
},
|
|
399
|
+
isCompanyOwner: (req, res, next) => {
|
|
400
|
+
if (!isValidParamCompanyUserOrRespond(req, true)) return
|
|
401
|
+
else next()
|
|
402
|
+
},
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function isAdminUser(req) {
|
|
406
|
+
return (req.user?.type?.match(/admin/) || req.user?.isAdmin) ? true : false
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function isValidUserOrRespond(req, res) {
|
|
410
|
+
// Check if the user is logged in, and that the requesting user is the same as the user, the requesting user might be outdated.
|
|
411
|
+
// E.g. new tab > signout > signin to a different user, now old tab needs to refresh.
|
|
412
|
+
if (!req.user) {
|
|
413
|
+
res.unauthorized('Please sign in first.')
|
|
414
|
+
return false
|
|
415
|
+
} else if (req.headers.requestingUserId && req.user._id?.toString() != req.headers.requestingUserId) {
|
|
416
|
+
res.unauthorized('Invalid session, please refresh your browser.')
|
|
417
|
+
return false
|
|
418
|
+
} else {
|
|
419
|
+
return true
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function isValidParamCompanyUserOrRespond(req, res, checkIsOwner = false) {
|
|
424
|
+
const _company = req.user?.company?._id?.toString() == req.params.cid ? req.user.company : false
|
|
425
|
+
const company = _company || req.user?.companies?.find((o) => o._id.toString() == req.params.cid)
|
|
426
|
+
const isCompanyOwner = company?.users?.find((o) => o._id.toString() == req.user?._id?.toString() && o.type === 'owner')
|
|
427
|
+
if (!isValidUserOrRespond(req, res)) return
|
|
428
|
+
else if (!isAdminUser(req) && !company) res.unauthorized('You are not authorised to make this request.')
|
|
429
|
+
else if (!isAdminUser(req) && checkIsOwner && !isCompanyOwner) res.unauthorized('Only owners can make this request.')
|
|
430
|
+
else return true
|
|
415
431
|
}
|
package/types/server/index.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ import userModel from './models/user.js';
|
|
|
13
13
|
import companyModel from './models/company.js';
|
|
14
14
|
export function setupDefaultModels(db: any): Promise<void>;
|
|
15
15
|
export { userModel, companyModel };
|
|
16
|
-
export { setupRouter, middleware } from "./router.js";
|
|
16
|
+
export { setupRouter, middleware, isValidUserOrRespond, isAdminUser } from "./router.js";
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
package/types/server/router.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export function setupRouter(config: any): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
2
|
+
export function isAdminUser(req: any): boolean;
|
|
3
|
+
export function isValidUserOrRespond(req: any, res: any): boolean;
|
|
2
4
|
/** @type {MiddlewareConfig} */
|
|
3
5
|
export const middleware: MiddlewareConfig;
|
|
4
6
|
export type Request = express.Request & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../server/router.js"],"names":[],"mappings":"AAmCA,wHA6HC;
|
|
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"}
|