@tmlmobilidade/fastify 20260616.2252.19 → 20260617.1703.12

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.
@@ -52,8 +52,9 @@ export function authorizationMiddleware(scope, actions, requireAll = false) {
52
52
  const isAllowed = requireAll
53
53
  ? permissionChecks.every(Boolean) // all must be true
54
54
  : permissionChecks.some(Boolean); // at least one must be true
55
- if (!isAllowed)
55
+ if (!isAllowed) {
56
56
  throw new HttpException(HTTP_STATUS.FORBIDDEN, `Insufficient permissions | User: ${request.me._id} | Scope: "${scope}" | Actions: [${actions.join(',')}]`);
57
+ }
57
58
  //
58
59
  };
59
60
  }
@@ -21,6 +21,11 @@ export interface FastifyServiceOptions extends FastifyServerOptions {
21
21
  * @default '0.0.0.0'
22
22
  */
23
23
  host?: string;
24
+ /**
25
+ * The module name for the Fastify server.
26
+ * @default 'fastify'
27
+ */
28
+ module?: string;
24
29
  /**
25
30
  * The origin for CORS requests.
26
31
  * Defaults to `true` if not provided.
@@ -61,7 +66,7 @@ export declare class FastifyService {
61
66
  * @return A promise that resolves to the URL of the Fastify server.
62
67
  * @throws Will throw an error if the server fails to start.
63
68
  */
64
- start(): Promise<string>;
69
+ start(moduleName?: string): Promise<string>;
65
70
  /**
66
71
  * Stops the Fastify server.
67
72
  * @return A promise that resolves when the server is stopped.
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
1
2
  /* * */
2
3
  import '@fastify/cors';
3
4
  import '@fastify/cookie';
@@ -7,24 +8,29 @@ import fastifyCookie from '@fastify/cookie';
7
8
  import fastifyCors from '@fastify/cors';
8
9
  import oneLineLogger from '@fastify/one-line-logger';
9
10
  import { HTTP_STATUS, HttpException } from '@tmlmobilidade/consts';
11
+ import { Logger } from '@tmlmobilidade/logger';
12
+ import { initSentryNode } from '@tmlmobilidade/logger';
10
13
  import fastify from 'fastify';
11
14
  const defaultFastifyServiceOptions = {
12
15
  bodyLimit: 1024 * 1024 * 10, // 10MB
13
16
  host: '0.0.0.0',
14
17
  logger: true,
18
+ module: 'fastify',
15
19
  origin: true,
16
20
  port: 5050,
17
21
  routerOptions: {
18
22
  ignoreTrailingSlash: true,
19
23
  },
20
24
  };
21
- const loggerOptions = {
25
+ const createLoggerOptions = (getModuleName) => ({
22
26
  level: 'debug',
27
+ module: getModuleName(),
23
28
  stream: oneLineLogger({
24
29
  colorize: true, // nice colors,
25
30
  colorizeObjects: true,
26
31
  messageFormat(log, messageKey, _, extras) {
27
32
  const c = extras.colors;
33
+ const moduleName = getModuleName();
28
34
  const palette = {
29
35
  error: c.redBright,
30
36
  highlight: c.yellowBright, // URLs / routes
@@ -96,10 +102,24 @@ const loggerOptions = {
96
102
  palette.pathLabel(`Path: ${palette.path(path)}`),
97
103
  message,
98
104
  ];
99
- return palette.pipe(parts.join(' | ')) + stackTrace;
105
+ const logMessage = palette.pipe(parts.join(' | ')) + stackTrace;
106
+ const shouldSendToSentry = message !== 'incoming request' && message !== 'request completed';
107
+ if (shouldSendToSentry) {
108
+ Logger.startNodeLogs({
109
+ app: 'api',
110
+ message: message,
111
+ method: method,
112
+ module: moduleName,
113
+ path: path,
114
+ reqId: reqId,
115
+ severity: 'info',
116
+ status: statusCode,
117
+ });
118
+ }
119
+ return logMessage;
100
120
  },
101
121
  }),
102
- };
122
+ });
103
123
  /**
104
124
  * FastifyService is a singleton class that provides a Fastify server instance.
105
125
  * It allows for setting up routes, plugins, and starting/stopping the server.
@@ -117,8 +137,8 @@ export class FastifyService {
117
137
  */
118
138
  constructor(options) {
119
139
  const mergedOptions = { ...defaultFastifyServiceOptions, ...options };
120
- this.server = fastify({ ...mergedOptions, logger: loggerOptions });
121
140
  this.options = mergedOptions;
141
+ this.server = fastify({ ...mergedOptions, logger: createLoggerOptions(() => this.options.module ?? 'fastify') });
122
142
  this._setupDefaultRoutes();
123
143
  this._setupPlugins();
124
144
  }
@@ -141,16 +161,25 @@ export class FastifyService {
141
161
  * @return A promise that resolves to the URL of the Fastify server.
142
162
  * @throws Will throw an error if the server fails to start.
143
163
  */
144
- async start() {
164
+ async start(moduleName) {
165
+ if (moduleName)
166
+ this.options.module = moduleName;
167
+ try {
168
+ await initSentryNode();
169
+ }
170
+ catch (error) {
171
+ this.server.log.error({ err: error }, 'Error sending startup log to Sentry.');
172
+ }
145
173
  try {
146
- const serverUrl = await this.server.listen({ host: this.options.host, port: this.options.port });
174
+ const serverUrl = await this.server.listen({
175
+ host: this.options.host,
176
+ port: this.options.port,
177
+ });
147
178
  this.server.log.info(`Server is running at ${serverUrl}`);
148
- this.server.log.info(`CORS enabled for origin: ${this.options.origin}`);
149
- this.server.log.info(`Listening on ${this.options.host}:${this.options.port}`);
150
179
  return serverUrl;
151
180
  }
152
181
  catch (error) {
153
- this.server.log.error({ error, message: 'Error starting server.' });
182
+ this.server.log.error({ err: error }, 'Error starting server.');
154
183
  process.exit(1);
155
184
  }
156
185
  }
@@ -190,12 +219,15 @@ export class FastifyService {
190
219
  * If so, it sends a response with the appropriate status code and error message.
191
220
  * This ensures consistent error responses for HTTP exceptions throughout the application.
192
221
  */
193
- this.server.setErrorHandler((error, _, reply) => {
222
+ this.server.setErrorHandler((error, request, reply) => {
194
223
  // Log the error with full stack trace
195
224
  const errorMessage = error instanceof Error ? error.message : 'Unhandled error';
196
225
  this.server.log.error({ err: error }, errorMessage);
197
226
  // Handle HttpException errors
198
227
  if (error instanceof HttpException) {
228
+ if (error.statusCode === HTTP_STATUS.INTERNAL_SERVER_ERROR) {
229
+ Logger.issue({ context: { action: 'errorHandler', feature: this.options.module, request, value: request.body }, level: 'error', messageOrError: error });
230
+ }
199
231
  reply
200
232
  .status(error.statusCode)
201
233
  .send({
@@ -205,6 +237,7 @@ export class FastifyService {
205
237
  });
206
238
  }
207
239
  else {
240
+ Logger.issue({ context: { action: 'errorHandler', feature: this.options.module, request, value: request.body }, level: 'error', messageOrError: 'Internal server error' });
208
241
  reply
209
242
  .status(HTTP_STATUS.INTERNAL_SERVER_ERROR)
210
243
  .send({
@@ -228,8 +261,7 @@ export class FastifyService {
228
261
  const payloadJson = JSON.parse(payload);
229
262
  reply.code(payloadJson.statusCode ?? HTTP_STATUS.OK);
230
263
  }
231
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
232
- catch (error) {
264
+ catch {
233
265
  // Do nothing
234
266
  }
235
267
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/fastify",
3
- "version": "20260616.2252.19",
3
+ "version": "20260617.1703.12",
4
4
  "author": {
5
5
  "email": "iso@tmlmobilidade.pt",
6
6
  "name": "TML-ISO"
@@ -42,6 +42,7 @@
42
42
  "@fastify/one-line-logger": "^2.1.0",
43
43
  "@tmlmobilidade/consts": "*",
44
44
  "@tmlmobilidade/interfaces": "*",
45
+ "@tmlmobilidade/logger": "*",
45
46
  "@tmlmobilidade/utils": "*",
46
47
  "fastify": "5.8.5"
47
48
  },