Skip to content

Commit 3b155c2

Browse files
committed
feat: added authenticated router, moved to using ab-express library
1 parent 0d6e9ff commit 3b155c2

File tree

8 files changed

+2018
-587
lines changed

8 files changed

+2018
-587
lines changed

example-app/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
"test:e2e": "jest --config ./test/jest-e2e.json"
1919
},
2020
"dependencies": {
21-
"admin-bro": "^3.0.0-beta.12",
21+
"@admin-bro/express": "^3.0.0-beta.3",
2222
"@admin-bro/mongoose": "^1.0.0-beta.3",
2323
"@nestjs/common": "^6.7.2",
2424
"@nestjs/core": "^6.7.2",
2525
"@nestjs/mongoose": "^7.0.2",
2626
"@nestjs/platform-express": "^6.7.2",
27+
"admin-bro": "^3.0.0-beta.12",
28+
"express": "^4.17.1",
29+
"express-formidable": "^1.2.0",
30+
"express-session": "^1.17.1",
2731
"mongoose": "^5.9.27",
2832
"reflect-metadata": "^0.1.13",
2933
"rimraf": "^3.0.0",

example-app/src/app.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { MongooseSchemasModule } from './mongoose/mongoose.module';
2727
],
2828
},
2929
auth: {
30-
authenticate: async () => new Promise(() => ({ email: 'mordeczka' })),
30+
authenticate: async (email, password) => Promise.resolve({ test: 'test' }),
3131
cookieName: 'test',
3232
cookiePassword: 'testPass',
3333
},

example-app/src/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const bodyParser = require('body-parser')
2+
const formidable = require('express-formidable')
3+
4+
5+
const express = require('express')
6+
const app = express()
7+
8+
app.use(bodyParser.json());
9+
10+
const router = express.Router()
11+
router.use(formidable())
12+
13+
app.use('/router', router);
14+
15+
console.log(app._router.stack)
16+
17+
const run = async () => {
18+
// const mongooseConnection = await mongoose.connect(process.env.MONGO_URL)
19+
app.listen(8080, () => console.log('AdminBro is under localhost:8080/admin'))
20+
}
21+
22+
run()

example-app/yarn.lock

Lines changed: 1935 additions & 32 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
"@commitlint/cli": "^8.3.5",
2424
"@commitlint/config-conventional": "^8.3.4",
2525
"@semantic-release/git": "^9.0.0",
26-
"@types/express-formidable": "^1.0.4",
27-
"@types/express-session": "^1.17.0",
2826
"@typescript-eslint/eslint-plugin": "^3.7.0",
2927
"@typescript-eslint/parser": "^3.7.0",
3028
"admin-bro": ">=3.0.0-beta.7",
@@ -45,9 +43,7 @@
4543
"dependencies": {
4644
"@nestjs/common": "^7.4.2",
4745
"@nestjs/core": "^7.4.2",
48-
"express": "^4.17.1",
49-
"express-formidable": "^1.2.0",
50-
"express-session": "^1.17.1",
46+
"@types/express-session": "^1.17.0",
5147
"reflect-metadata": "^0.1.13",
5248
"rxjs": "^6.5.3"
5349
}

src/admin.module.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ export class AdminModule implements OnModuleInit {
3333
}
3434
}
3535

36-
public async onModuleInit() {
36+
public onModuleInit() {
3737
const admin = new AdminBro(this.adminModuleOptions.adminBroOptions);
38-
await admin.initialize();
3938

4039
const { httpAdapter } = this.httpAdapterHost;
4140
this.loader.register(admin, httpAdapter, {

src/loaders/express.loader.ts

Lines changed: 37 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import path from 'path';
2-
3-
import express from 'express';
4-
import formidable from 'express-formidable';
5-
import AdminBro, { Router } from 'admin-bro';
1+
/* eslint-disable no-underscore-dangle */
2+
import AdminBro from 'admin-bro';
63
import { Injectable } from '@nestjs/common';
74
import { AbstractHttpAdapter } from '@nestjs/core';
85
import { loadPackage } from '@nestjs/common/utils/load-package.util';
@@ -20,144 +17,49 @@ export class ExpressLoader extends AbstractLoader {
2017
) {
2118
const app = httpAdapter.getInstance();
2219

23-
const router = express.Router();
20+
loadPackage('express', '@admin-bro/nestjs');
21+
const adminBroExpressjs = loadPackage('@admin-bro/express', '@admin-bro/nestjs', () =>
22+
require('@admin-bro/express')
23+
);
24+
loadPackage('express-formidable', '@admin-bro/nestjs');
25+
26+
let jsonParser;
27+
let urlencodedParser;
28+
29+
// Nestjs uses bodyParser under the hood which is in conflict with admin-bro setup.
30+
// Due to admin-bro-expressjs usage of formidable we have to move body parser in layer tree after admin-bro init.
31+
// Notice! This is not documented feature of express, so this may change in the future. We have to keep an eye on it.
32+
if (app && app._router && app._router.stack) {
33+
const jsonParserIndex = app._router.stack.findIndex(layer => layer.name === 'jsonParser');
34+
if (jsonParserIndex >= 0) {
35+
jsonParser = app._router.stack.splice(jsonParserIndex, 1);
36+
}
2437

25-
router.use(formidable(options.formidableOptions));
38+
const urlencodedParserIndex = app._router.stack.findIndex(layer => layer.name === 'urlencodedParser');
39+
if (urlencodedParserIndex >= 0) {
40+
urlencodedParser = app._router.stack.splice(urlencodedParserIndex, 1);
41+
}
42+
}
2643

27-
const { assets, routes } = Router;
44+
let router;
2845

2946
if ('auth' in options) {
30-
this.configureSession(router, admin, options);
47+
loadPackage('express-session', '@admin-bro/nestjs');
48+
router = adminBroExpressjs.buildAuthenticatedRouter(
49+
admin, options.auth, undefined, options.sessionOptions, options.formidableOptions
50+
);
51+
} else {
52+
router = adminBroExpressjs.buildRouter(admin, undefined, options.formidableOptions);
3153
}
3254

33-
this.registerAssets(router, assets, options);
34-
this.registerRoutes(router, admin, routes, options);
35-
3655
app.use(options.adminBroOptions.rootPath, router);
37-
}
38-
39-
private registerAssets(app, assets, options: AdminModuleOptions) {
40-
assets.forEach(asset => {
41-
app.get(asset.path, (_req, res) => {
42-
res.sendFile(path.resolve(asset.src))
43-
})
44-
})
45-
}
4656

47-
private registerRoutes(app, admin: AdminBro, routes, options: AdminModuleOptions) {
48-
routes.forEach((route) => {
49-
// we have to change routes defined in AdminBro from {recordId} to :recordId
50-
const expressPath = `${route.path.replace(/{/g, ':').replace(/}/g, '') as string}`
51-
52-
const handler = async (req, res, next) => {
53-
try {
54-
const controller = new route.Controller({ admin }, req.session && req.session.adminUser)
55-
const { params, query } = req
56-
const method = req.method.toLowerCase()
57-
const payload = {
58-
...(req.fields || {}),
59-
...(req.files || {}),
60-
}
61-
const html = await controller[route.action]({
62-
...req,
63-
params,
64-
query,
65-
payload,
66-
method,
67-
}, res)
68-
if (route.contentType) {
69-
res.set({ 'Content-Type': route.contentType })
70-
}
71-
if (html) {
72-
res.send(html)
73-
}
74-
} catch (e) {
75-
next(e)
76-
}
77-
}
78-
79-
if (route.method === 'GET') {
80-
app.get(expressPath, handler)
81-
}
82-
83-
if (route.method === 'POST') {
84-
app.post(expressPath, handler)
85-
}
86-
})
87-
}
88-
89-
private configureSession(app, admin: AdminBro, options: AdminModuleOptions) {
90-
const session = loadPackage('express-session', '@admin-bro/nestjs', () =>
91-
require('express-session')
92-
);
93-
94-
app.use(session({
95-
...options.sessionOptions,
96-
secret: options.auth?.cookiePassword,
97-
name: options.auth?.cookieName || 'adminbro',
98-
}));
57+
if (jsonParser) {
58+
app._router.stack.push(...jsonParser);
59+
}
9960

100-
const { rootPath } = admin.options;
101-
let { loginPath, logoutPath } = admin.options;
102-
loginPath = loginPath.replace(rootPath, '')
103-
logoutPath = logoutPath.replace(rootPath, '')
104-
105-
app.get(loginPath, async (_req, res) => {
106-
const login = await admin.renderLogin({
107-
action: admin.options.loginPath,
108-
errorMessage: null,
109-
});
110-
res.send(login);
111-
});
112-
113-
app.post(loginPath, async (req, res, next) => {
114-
console.log('login post');
115-
const { email, password } = req.fields;
116-
const adminUser = await options.auth?.authenticate(email, password);
117-
if (adminUser) {
118-
req.session.adminUser = adminUser;
119-
req.session.save((err) => {
120-
if (err) {
121-
next(err);
122-
}
123-
if (req.session.redirectTo) {
124-
res.redirect(req.session.redirectTo);
125-
} else {
126-
res.redirect(rootPath);
127-
}
128-
})
129-
} else {
130-
const login = await admin.renderLogin({
131-
action: admin.options.loginPath,
132-
errorMessage: 'invalidCredentials',
133-
});
134-
res.send(login);
135-
}
136-
});
137-
138-
app.use((req, res, next) => {
139-
console.log('redirect?')
140-
if (AdminBro.Router.assets.some(asset => req.originalUrl.match(asset.path))) {
141-
next();
142-
} else if (req.session.adminUser) {
143-
next();
144-
} else {
145-
// If the redirection is caused by API call to some action just redirect to resource
146-
const [redirectTo] = req.originalUrl.split('/actions');
147-
req.session.redirectTo = redirectTo.includes(`${rootPath}/api`) ? rootPath : redirectTo;
148-
req.session.save((err) => {
149-
if (err) {
150-
next(err);
151-
}
152-
res.redirect(admin.options.loginPath);
153-
})
154-
}
155-
})
156-
157-
app.get(logoutPath, (req, res) => {
158-
req.session.destroy(() => {
159-
res.redirect(admin.options.loginPath)
160-
});
161-
});
61+
if (urlencodedParser) {
62+
app._router.stack.push(...urlencodedParser);
63+
}
16264
}
16365
}

0 commit comments

Comments
 (0)