UNPKG

7.71 kBPlain TextView Raw
1import * as express from "express";
2import App from "./app";
3import SkipResponse from "./skip.response";
4import { ValidateObject } from "./validate-object";
5import { HttpVerb } from "./http-verb";
6import { LynxControllerMetadata, LynxRouteMetadata } from "./decorators";
7import { BaseMiddleware, BLOCK_CHAIN } from "./base.middleware";
8
9import { logger } from "./logger";
10
11function retrieveArgumentsNamesFromRoute(path: string) {
12 const args = [];
13 const parts = path.split("/");
14 for (let part of parts) {
15 if (part.startsWith(":")) {
16 let arg = part.substring(1, part.length);
17 let endIndex = arg.indexOf("(");
18 if (endIndex != -1) {
19 arg = arg.substring(0, endIndex);
20 }
21 args.push(arg);
22 }
23 }
24 return args;
25}
26
27function generateStandardCallback(controller: any, route: LynxRouteMetadata, app: App) {
28 return async (
29 req: express.Request,
30 res: express.Response,
31 next: express.NextFunction
32 ) => {
33 (req as any).lynx = {
34 route: route
35 };
36 if (route.verifiers) {
37 for (let verify of route.verifiers) {
38 let passed = true;
39 if (verify.isAsync) {
40 passed = await verify.fun(req, res);
41 } else {
42 if (verify.fun) {
43 passed = verify.fun(req, res);
44 } else {
45 try {
46 passed = (verify as any)(req, res);
47 } catch(e) {
48 logger.error(e);
49 }
50 }
51 }
52 if (!passed) {
53 return next();
54 }
55 }
56 }
57
58 if (!controller._hasBeenInit) {
59 await controller.postConstructor();
60 }
61
62 let f = controller[route.method];
63 let argsNames = retrieveArgumentsNamesFromRoute(route.path);
64 let argsValues = [];
65 for (let arg of argsNames) {
66 argsValues.push(req.params[arg]);
67 }
68 if (route.body != null) {
69 let b = req.body;
70 if (route.body.schema) {
71 b = new ValidateObject(
72 b,
73 route.body.schema,
74 req.acceptsLanguages()
75 );
76 }
77 argsValues.push(b);
78 }
79 argsValues.push(req);
80 argsValues.push(res);
81 controller._ctxMap = {};
82 f.apply(controller, argsValues)
83 .then((r: any) => {
84 if (!r) {
85 logger.error("Wait, you have a method in a controller without any return!!");
86 logger.error(`Method info: ${route.type} ${route.path}`);
87 throw new Error("Method without any return!");
88 }
89 if (route.isAPI) {
90 let body = app.apiResponseWrapper.onSuccess(r);
91 return res.send(body);
92 } else {
93 if (r instanceof SkipResponse) {
94 r.performResponse(req, res);
95 return next();
96 }
97 if (r.performResponse) {
98 return r.performResponse(req, res);
99 }
100 res.send(r);
101 }
102 })
103 .catch((error: Error) => {
104 logger.info(error);
105 let status = 400;
106 let e = error as any;
107 if (e.statusCode) {
108 status = e.statusCode;
109 }
110 if (!res.headersSent) {
111 res.status(status);
112 }
113 if (route.isAPI) {
114 let body = app.apiResponseWrapper.onError(error);
115 res.send(body);
116 } else {
117 next(error);
118 }
119 });
120 };
121}
122
123export function generateRouter(
124 app: App,
125 controller: any,
126 originalController: LynxControllerMetadata,
127 routes: any
128): express.Router {
129 const router = express.Router();
130
131 for (let route of originalController.routes) {
132 if (route.isDisabledOn) {
133 if (route.isDisabledOn()) {
134 continue;
135 }
136 }
137 let func: Function;
138 switch (route.type) {
139 case HttpVerb.GET:
140 func = router.get;
141 break;
142 case HttpVerb.POST:
143 func = router.post;
144 break;
145 case HttpVerb.PUT:
146 func = router.put;
147 break;
148 case HttpVerb.DELETE:
149 func = router.delete;
150 break;
151 case HttpVerb.PATCH:
152 func = router.patch;
153 break;
154 default:
155 throw new Error(
156 "The decoration type for the method " +
157 route.method +
158 " is invalid"
159 );
160 }
161 if (route.name) {
162 routes[route.name] = (
163 originalController.controllerPath +
164 "/" +
165 route.path
166 ).replace(/\/\/+/g, "/");
167 }
168 const callback = generateStandardCallback(controller, route, app);
169 let p = route.path;
170 if (!p.startsWith("/")) {
171 p = "/" + p;
172 }
173 if (route.isMultipartForm) {
174 func.call(router, p, app.upload.any(), callback);
175 } else {
176 func.call(router, p, callback);
177 }
178 }
179
180 return router;
181}
182
183export function useController(
184 app: App,
185 Controller: LynxControllerMetadata,
186 routes: any
187) {
188 if (!Controller.controllerPath) {
189 throw new Error(
190 'You should decorate the "' +
191 (<any>Controller).name +
192 '" class in order to use it'
193 );
194 }
195 const controller = new (<any>Controller)(app);
196 controller._metadata = Controller;
197 const router = generateRouter(app, controller, Controller, routes);
198 app.express.use(Controller.controllerPath, router);
199}
200
201function generateMiddlewareCallback(middleware: BaseMiddleware) {
202 return (
203 req: express.Request,
204 res: express.Response,
205 next: express.NextFunction
206 ) => {
207 let f = middleware.apply;
208 let argsValues = [];
209 argsValues.push(req);
210 argsValues.push(res);
211 f.apply(middleware, argsValues)
212 .then((r: any) => {
213 if (r === BLOCK_CHAIN) {
214 return;
215 }
216 next();
217 })
218 .catch((error: Error) => {
219 logger.info(error);
220 let status = 400;
221 let e = error as any;
222 if (e.statusCode) {
223 status = e.statusCode;
224 }
225 res.status(status).send(error);
226 });
227 };
228}
229
230function generateMiddlewares(
231 app: App,
232 middleware: BaseMiddleware,
233 path: string
234) {
235 const callback = generateMiddlewareCallback(middleware);
236 app.express.use(path, callback);
237}
238
239export function useMiddleware(app: App, Middleware: any) {
240 if (!Middleware.middlewarePath) {
241 throw new Error(
242 'You should use at least one Middleware() decorator to a method of the "' +
243 Middleware.name +
244 '" class.'
245 );
246 }
247 const middleware = new Middleware(app);
248 generateMiddlewares(
249 app,
250 middleware as BaseMiddleware,
251 Middleware.middlewarePath
252 );
253}