Skip to content

Commit b23e0c2

Browse files
author
hirsch88
committed
Merge branch 'release/1.7.0'
2 parents e04da91 + da37e1e commit b23e0c2

36 files changed

+794
-421
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ All script are defined in the package.json file, but the most important ones are
7070
### Console
7171
* To run your own created cli script enter `npm run console <command-name>`
7272

73+
## IoC
74+
Our IoC automatically looks through the `controllers`, `listeners` , `middlewares`, `services`,
75+
`repositories` and `models` for files to bind to our IoC - Container. So you have nothing to do.
76+
77+
However it is very important to keep the naming, because otherwise our IoC will not find your
78+
created file.
79+
7380
## Using the debugger in VS Code
7481
Just set a breakpoint and hit `F5` in your Visual Studio Code.
7582

@@ -92,15 +99,17 @@ The route prefix is by default `/api/v1`, but you can change this in the .env.ex
9299
| **src/** | Source-Files |
93100
| **src/api/controllers/** | REST-API - Controllers |
94101
| **src/api/exceptions/** | Exceptions like 404 NotFound |
102+
| **src/api/listeners/** | Event-Listeners |
95103
| **src/api/middlewares/** | Express Middlewares like populateUser |
96104
| **src/api/models/** | Bookshelf Models |
97105
| **src/api/repositories/** | Repository Layer |
98106
| **src/api/requests/** | Request Bodys with Validations |
99107
| **src/api/services/** | Service Layer |
100108
| **src/api/** swagger.json | Swagger Documentation |
101109
| **src/console/** | Command Line scripts |
110+
| **src/config/** | Configurations like database or logger |
102111
| **src/constants/** | Global Constants |
103-
| **src/core/** | All the libraries configurations and our small framework |
112+
| **src/core/** | Our small framework |
104113
| **src/database/factories/** | Model Factories to generate database records |
105114
| **src/database/migrations/** | Migrations scripts to build up our database schema |
106115
| **src/database/seeds/** | Seed scripts to fake some data into our database |

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "express-typescript-boilerplate",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"description": "A delightful way to building a RESTful API with NodeJs & TypeScript",
55
"main": "src/index.ts",
66
"scripts": {
@@ -61,6 +61,7 @@
6161
"@types/dotenv": "^4.0.0",
6262
"@types/express": "^4.0.35",
6363
"@types/faker": "^4.1.0",
64+
"@types/glob": "^5.0.30",
6465
"@types/helmet": "0.0.35",
6566
"@types/jest": "^19.2.3",
6667
"@types/jsonwebtoken": "^7.2.0",
@@ -85,6 +86,7 @@
8586
"express": "^4.15.3",
8687
"express-status-monitor": "^0.1.9",
8788
"faker": "^4.1.0",
89+
"glob": "^7.1.2",
8890
"helmet": "^3.6.1",
8991
"inversify": "^4.1.1",
9092
"inversify-express-utils": "^3.5.1",
@@ -122,7 +124,8 @@
122124
"js",
123125
"json"
124126
],
125-
"testEnvironment": "node"
127+
"testEnvironment": "node",
128+
"setupTestFrameworkScriptFile": "./test/unit/lib/setup.ts"
126129
},
127130
"license": "MIT"
128131
}

src/api/controllers/UserController.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
import { injectable, inject, named } from 'inversify';
1+
/**
2+
* UserController
3+
* ----------------------------------------
4+
*
5+
* This controller is in charge of the user resource and should
6+
* provide all crud actions.
7+
*/
8+
9+
import { inject, named } from 'inversify';
210
import { Controller, Get, Post, Put, Delete, RequestParam, RequestBody, Response, Request } from 'inversify-express-utils';
311
import { my } from 'my-express';
4-
import { UserService } from '../services/UsersService';
12+
import { UserService } from '../services/UserService';
513
import { Types } from '../../constants/Types';
6-
import { Service } from '../../constants/Targets';
7-
import { authenticate, populateUser } from '../middlewares';
14+
import { Service, Middleware } from '../../constants/Targets';
15+
import { AuthenticateMiddleware } from '../middlewares/AuthenticateMiddleware';
16+
import { PopulateUserMiddleware } from '../middlewares/PopulateUserMiddleware';
17+
import { ioc } from '../../core/IoC';
818

9-
/**
10-
* UserController is in charge of the user resource and should
11-
* provide all crud actions.
12-
*/
13-
@injectable()
14-
@Controller('/user', authenticate)
19+
// Get middlewares
20+
const authenticate = ioc.Container.getNamed<AuthenticateMiddleware>(Types.Middleware, Middleware.AuthenticateMiddleware);
21+
const populateUser = ioc.Container.getNamed<PopulateUserMiddleware>(Types.Middleware, Middleware.PopulateUserMiddleware);
22+
23+
24+
@Controller('/user', authenticate.use)
1525
export class UserController {
1626

1727
constructor( @inject(Types.Service) @named(Service.UserService) private userService: UserService) { }
@@ -28,7 +38,7 @@ export class UserController {
2838
return res.created(user.toJSON());
2939
}
3040

31-
@Get('/me', populateUser)
41+
@Get('/me', populateUser.use)
3242
public async findMe( @Request() req: my.Request, @Response() res: my.Response): Promise<any> {
3343
return res.found(req.user);
3444
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
3+
export namespace auth {
4+
5+
export class AuthController {
6+
7+
}
8+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { inject, named } from 'inversify';
2+
import { Types } from '../../constants/Types';
3+
import { Core } from '../../constants/Targets';
4+
import { Log } from '../../core/log/';
5+
6+
7+
export class UserAuthenticatedListener {
8+
9+
static Event = Symbol('UserAuthenticatedListener');
10+
11+
public log: Log;
12+
13+
constructor(
14+
@inject(Types.Core) @named(Core.Log) Logger: typeof Log
15+
) {
16+
this.log = new Logger('api:listeners:UserAuthenticatedListener');
17+
}
18+
19+
public async run(user: any): Promise<void> {
20+
this.log.info('Receive event UserAuthenticatedListener', user);
21+
}
22+
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { inject, named } from 'inversify';
2+
import { Types } from '../../constants/Types';
3+
import { Core } from '../../constants/Targets';
4+
import { Log } from '../../core/log/';
5+
6+
const log = new Log('api:listeners:UserCreated');
7+
8+
9+
export class UserCreatedListener {
10+
11+
static Event = Symbol('UserCreated');
12+
13+
public log: Log;
14+
15+
constructor(
16+
@inject(Types.Core) @named(Core.Log) Logger: typeof Log
17+
) {
18+
this.log = new Logger('api:listeners:UserCreatedListener');
19+
}
20+
21+
public async run(user: any): Promise<void> {
22+
log.info('Receive event UserCreated', user);
23+
}
24+
25+
}
26+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { inject, named } from 'inversify';
2+
import * as Request from 'request';
3+
import { my } from 'my-express';
4+
import { Log } from '../../core/log';
5+
import { Types } from '../../constants/Types';
6+
import { Lib, Core } from '../../constants/Targets';
7+
import { events } from '../../core/api/events';
8+
import { UserAuthenticatedListener } from '../listeners/UserAuthenticatedListener';
9+
10+
11+
export class AuthenticateMiddleware {
12+
13+
public log: Log;
14+
15+
constructor(
16+
@inject(Types.Core) @named(Core.Log) Logger: typeof Log,
17+
@inject(Types.Lib) @named(Lib.Request) private request: typeof Request
18+
) {
19+
this.log = new Logger('api:middleware:AuthenticateMiddleware');
20+
}
21+
22+
23+
public use = (req: my.Request, res: my.Response, next: my.NextFunction): void => {
24+
const token = this.getToken(req);
25+
26+
if (token === null) {
27+
this.log.warn('No token given');
28+
return res.failed(403, 'You are not allowed to request this resource!');
29+
}
30+
this.log.debug('Token is provided');
31+
32+
// Request user info at auth0 with the provided token
33+
this.request({
34+
method: 'POST',
35+
url: `${process.env.AUTH0_HOST}/tokeninfo`,
36+
form: {
37+
id_token: token
38+
}
39+
}, (error: any, response: Request.RequestResponse, body: any) => {
40+
// Verify if the requests was successful and append user
41+
// information to our extended express request object
42+
if (!error && response.statusCode === 200) {
43+
req.tokeninfo = JSON.parse(body);
44+
this.log.info(`Retrieved user ${req.tokeninfo.email}`);
45+
events.emit(UserAuthenticatedListener.Event, req.tokeninfo);
46+
return next();
47+
}
48+
49+
// Catch auth0 exception and return it as it is
50+
this.log.warn(`Could not retrieve the user, because of`, body);
51+
let statusCode = 401;
52+
if (response && response.statusCode) {
53+
statusCode = response.statusCode;
54+
} else {
55+
this.log.warn('It seems your oauth server is down!');
56+
}
57+
res.failed(statusCode, body);
58+
59+
});
60+
}
61+
62+
private getToken(req: my.Request): string | null {
63+
const authorization = req.headers.authorization;
64+
65+
// Retrieve the token form the Authorization header
66+
if (authorization && authorization.split(' ')[0] === 'Bearer') {
67+
return authorization.split(' ')[1];
68+
}
69+
70+
// No token was provided by the client
71+
return null;
72+
}
73+
74+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { inject, named } from 'inversify';
2+
import { my } from 'my-express';
3+
import { Log } from '../../core/log';
4+
import { UserService } from '../services/UserService';
5+
import { Types } from '../../constants/Types';
6+
import { Service, Core } from '../../constants/Targets';
7+
8+
9+
export class PopulateUserMiddleware {
10+
11+
public log: Log;
12+
13+
constructor(
14+
@inject(Types.Core) @named(Core.Log) Logger: typeof Log,
15+
@inject(Types.Service) @named(Service.UserService) private userService: UserService
16+
) {
17+
this.log = new Logger('api:middleware:PopulateUserMiddleware');
18+
}
19+
20+
21+
public use = (req: my.Request, res: my.Response, next: my.NextFunction): void => {
22+
// Check if the authenticate middleware was successful
23+
if (!req.tokeninfo || !req.tokeninfo.user_id) {
24+
return res.failed(400, 'Missing token information!');
25+
}
26+
// Find user from the token and store him in the request object
27+
this.userService.findByUserId(req.tokeninfo.user_id)
28+
.then((user) => {
29+
req.user = user.toJSON();
30+
this.log.debug(`populated user with the id=${req.user.id} to the request object`);
31+
next();
32+
})
33+
.catch((error) => {
34+
this.log.warn(`could not populate user to the request object`);
35+
next(error);
36+
});
37+
}
38+
39+
}

src/api/middlewares/authenticate.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/api/middlewares/index.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)