beavuck-time 2.3.13 → 2.4.1

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/README.md CHANGED
@@ -86,12 +86,19 @@ If you want to run this service programmatically in your Node.js project:
86
86
  ```js
87
87
  import { startServer } from 'beavuck-time'
88
88
 
89
- startServer()
89
+ startServer({
90
+ hostUrl: 'https://time-api.example.com',
91
+ trustedOrigins: 'https://my.app.com, https://my.app.com/*, https://my-other.app.com, https://my-other.app.com/*',
92
+ apiPort: 3000,
93
+ rateLimit: -1,
94
+ logLevel: 'info',
95
+ maxLogFiles: 64,
96
+ maxSizeLogFiles: '1m',
97
+ })
90
98
  ```
91
99
 
92
- You can rely on environment variables (BEAVUCK_TIME_HOST_URL, etc.) stored in a `.env` file at the root of your project.
93
- It is a known limitation -- work is in progress to allow passing those options directly as parameters to `startServer()`.
94
- Refer to the Docker Compose example below to see an exhaustive list of available environment variables and what they do.
100
+ You can rely on environment variables (BEAVUCK_TIME_HOST_URL, etc.) instead of passing an options object. Refer to
101
+ the Docker Compose example below to see an exhaustive list of available environment variables and what they do.
95
102
 
96
103
  #### CLI
97
104
 
@@ -127,7 +134,7 @@ time:
127
134
  # BEAVUCK_TIME_TRUSTED_ORIGINS: To allow requests from any origin, include * (not recommended). If empty, will only allow requests from the HOST_URL's origin. Defaults to the HOST_URL's origin
128
135
  - BEAVUCK_TIME_API_PORT: 3000
129
136
  # BEAVUCK_TIME_API_PORT: Optional. Internal port when in a container. Defaults to 3000
130
- - BEAVUCK_TIME_RATE_LIMIT: 100
137
+ - BEAVUCK_TIME_RATE_LIMIT: -1
131
138
  # BEAVUCK_TIME_RATE_LIMIT: Optional. Max allowed number of requests per minute for each IP address. If negative or 0, no limit. Defaults to no limit
132
139
  - BEAVUCK_TIME_LOG_LEVEL: info
133
140
  # BEAVUCK_TIME_LOG_LEVEL: Optional. Logging levels include error, warn, info, http, verbose, debug, silly. Defaults to info
@@ -11,7 +11,7 @@ exports.getTrustedOrigins = getTrustedOrigins;
11
11
  exports.initTrustedOrigins = initTrustedOrigins;
12
12
  exports.isAllTrusted = isAllTrusted;
13
13
  exports.isOriginAbsentOrTrusted = isOriginAbsentOrTrusted;
14
- const CorsError_1 = require("../errors/CorsError");
14
+ const corsError_1 = require("../errors/corsError");
15
15
  const urlUtil_1 = require("../utils/urlUtil");
16
16
  const http_status_codes_1 = require("http-status-codes");
17
17
  const dotenvx_1 = __importDefault(require("@dotenvx/dotenvx"));
@@ -27,7 +27,7 @@ exports.corsOptions = {
27
27
  callback(null, true);
28
28
  }
29
29
  else {
30
- callback(new CorsError_1.CorsError(origin), false);
30
+ callback(new corsError_1.CorsError(origin), false);
31
31
  }
32
32
  },
33
33
  };
@@ -50,11 +50,16 @@ function getTrustedOrigins() {
50
50
  }
51
51
  function initTrustedOrigins() {
52
52
  const trustedOrigins = process.env.BEAVUCK_TIME_TRUSTED_ORIGINS;
53
- TRUSTED_ORIGINS = trustedOrigins.split(',');
53
+ TRUSTED_ORIGINS = trustedOrigins
54
+ .split(',')
55
+ .map(to => to.trim())
56
+ .filter(to => to.length > 0);
54
57
  }
55
58
  function isAllTrusted() {
56
59
  return getTrustedOrigins().includes('*');
57
60
  }
58
61
  function isOriginAbsentOrTrusted(origin) {
59
- return !origin || getTrustedOrigins().includes(origin);
62
+ return (!origin ||
63
+ getTrustedOrigins().includes(origin) ||
64
+ getTrustedOrigins().some(to => to.includes('/*') && origin.startsWith(to)));
60
65
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // src/errors/base/BeavuckTimeError.ts
2
+ // src/errors/base/beavuckTimeError.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.BeavuckTimeError = void 0;
5
5
  class BeavuckTimeError extends Error {
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
- // src/errors/BeavuckTimeClientError.ts
2
+ // src/errors/beavuckTimeClientError.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.BeavuckTimeClientError = void 0;
5
- const BeavuckTimeError_1 = require("./base/BeavuckTimeError");
5
+ const beavuckTimeError_1 = require("./base/beavuckTimeError");
6
6
  const http_status_codes_1 = require("http-status-codes");
7
- class BeavuckTimeClientError extends BeavuckTimeError_1.BeavuckTimeError {
7
+ class BeavuckTimeClientError extends beavuckTimeError_1.BeavuckTimeError {
8
8
  static baseMessage = 'Client Error';
9
9
  constructor(message, code = http_status_codes_1.StatusCodes.BAD_REQUEST) {
10
10
  super(`${BeavuckTimeClientError.baseMessage}: ${message}`, code);
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
- // src/errors/BeavuckTimeServerError.ts
2
+ // src/errors/beavuckTimeServerError.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.BeavuckTimeServerError = void 0;
5
- const BeavuckTimeError_1 = require("./base/BeavuckTimeError");
5
+ const beavuckTimeError_1 = require("./base/beavuckTimeError");
6
6
  const http_status_codes_1 = require("http-status-codes");
7
- class BeavuckTimeServerError extends BeavuckTimeError_1.BeavuckTimeError {
7
+ class BeavuckTimeServerError extends beavuckTimeError_1.BeavuckTimeError {
8
8
  static baseMessage = 'Internal Server Error';
9
9
  constructor(message, code = http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR) {
10
10
  super(`${BeavuckTimeServerError.baseMessage}: ${message}`, code);
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
- // src/errors/CorsError.ts
2
+ // src/errors/corsError.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.CorsError = void 0;
5
5
  const http_status_codes_1 = require("http-status-codes");
6
- const BeavuckTimeClientError_1 = require("./BeavuckTimeClientError");
7
- class CorsError extends BeavuckTimeClientError_1.BeavuckTimeClientError {
6
+ const beavuckTimeClientError_1 = require("./beavuckTimeClientError");
7
+ class CorsError extends beavuckTimeClientError_1.BeavuckTimeClientError {
8
8
  static baseMessage = 'Origin forbidden by CORS';
9
9
  constructor(origin) {
10
10
  super(`${CorsError.baseMessage}: ${origin}`, http_status_codes_1.StatusCodes.FORBIDDEN);
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ // src/errors/errorResponse.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ErrorResponse = void 0;
5
+ const beavuckTimeServerError_1 = require("./beavuckTimeServerError");
6
+ class ErrorResponse {
7
+ message;
8
+ details;
9
+ constructor(message = beavuckTimeServerError_1.BeavuckTimeServerError.baseMessage, details) {
10
+ this.message = message;
11
+ this.details = details;
12
+ }
13
+ }
14
+ exports.ErrorResponse = ErrorResponse;
@@ -7,9 +7,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.corsMiddleware = void 0;
8
8
  const cors_1 = __importDefault(require("cors"));
9
9
  const urlUtil_1 = require("../utils/urlUtil");
10
- const CorsError_1 = require("../errors/CorsError");
10
+ const corsError_1 = require("../errors/corsError");
11
11
  const logger_1 = require("../config/logger");
12
- const BeavuckTimeServerError_1 = require("../errors/BeavuckTimeServerError");
12
+ const beavuckTimeServerError_1 = require("../errors/beavuckTimeServerError");
13
13
  const corsOptions_1 = require("../config/corsOptions");
14
14
  const corsMiddleware = (req, res, next) => {
15
15
  const HOST_URL = (0, urlUtil_1.tryParseUrl)(process.env.BEAVUCK_TIME_HOST_URL ?? '');
@@ -21,14 +21,14 @@ const corsMiddleware = (req, res, next) => {
21
21
  }
22
22
  else if (!HOST_URL) {
23
23
  const noHostUrl = 'Host URL not set';
24
- next(new BeavuckTimeServerError_1.BeavuckTimeServerError(noHostUrl));
24
+ next(new beavuckTimeServerError_1.BeavuckTimeServerError(noHostUrl));
25
25
  }
26
26
  else if ((0, urlUtil_1.isSameOrigin)(HOST_URL, (0, urlUtil_1.tryParseUrl)(referrerHeader ?? ''))) {
27
27
  next();
28
28
  }
29
29
  else {
30
30
  const badOriginAndOrReferrer = `origin: ${req.headers.origin}, referrer: ${referrerHeader}`;
31
- next(new CorsError_1.CorsError(badOriginAndOrReferrer));
31
+ next(new corsError_1.CorsError(badOriginAndOrReferrer));
32
32
  }
33
33
  };
34
34
  exports.corsMiddleware = corsMiddleware;
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.errorHandler = void 0;
5
5
  const logger_1 = require("../config/logger");
6
- const BeavuckTimeClientError_1 = require("../errors/BeavuckTimeClientError");
7
- const BeavuckTimeServerError_1 = require("../errors/BeavuckTimeServerError");
6
+ const beavuckTimeClientError_1 = require("../errors/beavuckTimeClientError");
7
+ const beavuckTimeServerError_1 = require("../errors/beavuckTimeServerError");
8
8
  const http_status_codes_1 = require("http-status-codes");
9
9
  const tsoa_1 = require("tsoa");
10
+ const errorResponse_1 = require("../errors/errorResponse");
10
11
  const errorHandler = (err, req, res, next) => {
11
12
  if (err instanceof tsoa_1.ValidateError) {
12
13
  const logMsg = `Validation error on ${req.path}: ${err.fields}`;
@@ -14,11 +15,11 @@ const errorHandler = (err, req, res, next) => {
14
15
  const resMsgObj = { message: 'Validation Error', details: err.fields };
15
16
  handleOtherError(res, logMsg, resStatus, resMsgObj);
16
17
  }
17
- else if (err instanceof BeavuckTimeClientError_1.BeavuckTimeClientError) {
18
+ else if (err instanceof beavuckTimeClientError_1.BeavuckTimeClientError) {
18
19
  handleBeavuckClientError(res, err);
19
20
  }
20
- else if (err instanceof BeavuckTimeServerError_1.BeavuckTimeServerError || err instanceof Error) {
21
- handleBeavuckServerError(res, BeavuckTimeServerError_1.BeavuckTimeServerError.fromError(err));
21
+ else if (err instanceof beavuckTimeServerError_1.BeavuckTimeServerError || err instanceof Error) {
22
+ handleBeavuckServerError(res, beavuckTimeServerError_1.BeavuckTimeServerError.fromError(err));
22
23
  }
23
24
  else if (err) {
24
25
  handleOtherError(res);
@@ -39,9 +40,7 @@ function handleBeavuckServerError(res, err) {
39
40
  logger_1.logger.error(err);
40
41
  sendErrorResponse(res, err);
41
42
  }
42
- function handleOtherError(res, logMsg = `Unknown error`, resStatus = http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR, resMsg = {
43
- message: BeavuckTimeServerError_1.BeavuckTimeServerError.baseMessage,
44
- }) {
43
+ function handleOtherError(res, logMsg = `Unknown error`, resStatus = http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR, resMsg = new errorResponse_1.ErrorResponse()) {
45
44
  logger_1.logger.error(`${logMsg}: ${JSON.stringify(resMsg)}`);
46
45
  res.status(resStatus).json(resMsg);
47
46
  }
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.rateLimiter = void 0;
8
8
  const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
9
9
  const NO_RATE_LIMIT = '-1';
10
- const RATE_LIMIT = parseInt(process.env.BEAVUCK_TIME_RATE_LIMIT ?? NO_RATE_LIMIT, 10);
10
+ const RATE_LIMIT = Number.parseInt(process.env.BEAVUCK_TIME_RATE_LIMIT ?? NO_RATE_LIMIT, 10);
11
11
  const SECONDS_IN_ONE_MINUTE = 60;
12
12
  const MILLISECONDS_IN_ONE_SECOND = 1000;
13
13
  const ONE_MINUTE = SECONDS_IN_ONE_MINUTE * MILLISECONDS_IN_ONE_SECOND;
package/build/server.js CHANGED
@@ -10,26 +10,14 @@ const api_1 = require("./api");
10
10
  const logger_1 = require("./config/logger");
11
11
  const urlUtil_1 = require("./utils/urlUtil");
12
12
  const dotenvx_1 = __importDefault(require("@dotenvx/dotenvx"));
13
+ // Load environment variables from .env file, if available
14
+ // but don't error if the file is missing
13
15
  dotenvx_1.default.config({
14
16
  ignore: ['MISSING_ENV_FILE'],
15
17
  });
16
- function validateEnv({ hostUrl, trustedOrigins, }) {
17
- if (!hostUrl || !(0, urlUtil_1.tryParseUrl)(hostUrl)) {
18
- logger_1.logger.error('HOST_URL not set or is not a valid URL');
19
- process.exit(1);
20
- }
21
- if (!trustedOrigins) {
22
- logger_1.logger.error('TRUSTED_ORIGINS not set');
23
- process.exit(1);
24
- }
25
- }
26
- // FIXME the server options method causes an exception, npm users will have to use a .env file for now
27
18
  function startServer(options = {}) {
28
- const API_PORT = options.port ?? process.env.BEAVUCK_TIME_API_PORT ?? 3000;
29
- const HOST_URL = options.hostUrl ?? process.env.BEAVUCK_TIME_HOST_URL ?? '';
30
- const TRUSTED_ORIGINS = options.trustedOrigins ?? process.env.BEAVUCK_TIME_TRUSTED_ORIGINS ?? '';
31
- validateEnv({ hostUrl: HOST_URL, trustedOrigins: TRUSTED_ORIGINS });
32
- const server = api_1.api.listen(API_PORT, () => {
19
+ manageEnvVars(options);
20
+ const server = api_1.api.listen(process.env.BEAVUCK_TIME_API_PORT, () => {
33
21
  logger_1.logger.info(`
34
22
  | | | | ,=.
35
23
  | |__ ___ __ ___ ___ _ ___| | __ ,=""""==.__.=" o".___
@@ -40,7 +28,7 @@ function startServer(options = {}) {
40
28
 
41
29
  Beavuck Time microservice started successfully
42
30
 
43
- Ready on API port ${API_PORT} (if this is running in a container, this port number is internal to the container)
31
+ Ready on API port ${process.env.BEAVUCK_TIME_API_PORT} (if this is running in a container, this port number is internal to the container)
44
32
  `);
45
33
  });
46
34
  server.on('error', (err) => {
@@ -60,6 +48,39 @@ function startServer(options = {}) {
60
48
  process.on(SIGINT, () => gracefulShutdown(SIGINT));
61
49
  return server;
62
50
  }
51
+ function manageEnvVars(options) {
52
+ process.env.BEAVUCK_TIME_API_PORT =
53
+ options.apiPort === undefined
54
+ ? '3000'
55
+ : String(options.apiPort);
56
+ process.env.BEAVUCK_TIME_HOST_URL =
57
+ options.hostUrl ?? process.env.BEAVUCK_TIME_HOST_URL;
58
+ process.env.BEAVUCK_TIME_TRUSTED_ORIGINS =
59
+ options.trustedOrigins ?? process.env.BEAVUCK_TIME_TRUSTED_ORIGINS;
60
+ process.env.BEAVUCK_TIME_RATE_LIMIT =
61
+ options.rateLimit === undefined
62
+ ? process.env.BEAVUCK_TIME_RATE_LIMIT
63
+ : String(options.rateLimit);
64
+ process.env.BEAVUCK_TIME_LOG_LEVEL =
65
+ options.logLevel ?? process.env.BEAVUCK_TIME_LOG_LEVEL;
66
+ process.env.BEAVUCK_TIME_MAX_LOG_FILES =
67
+ options.maxLogFiles === undefined
68
+ ? process.env.BEAVUCK_TIME_MAX_LOG_FILES
69
+ : String(options.maxLogFiles);
70
+ process.env.BEAVUCK_TIME_MAX_SIZE_LOG_FILES =
71
+ options.maxSizeLogFiles ?? process.env.BEAVUCK_TIME_MAX_SIZE_LOG_FILES;
72
+ validateEnv();
73
+ }
74
+ function validateEnv() {
75
+ if (!process.env.BEAVUCK_TIME_HOST_URL || !(0, urlUtil_1.tryParseUrl)(process.env.BEAVUCK_TIME_HOST_URL)) {
76
+ logger_1.logger.error('HOST_URL not set or is not a valid URL');
77
+ process.exit(1);
78
+ }
79
+ if (!process.env.BEAVUCK_TIME_TRUSTED_ORIGINS) {
80
+ logger_1.logger.error('TRUSTED_ORIGINS not set');
81
+ process.exit(1);
82
+ }
83
+ }
63
84
  // Support running as standalone binary
64
85
  if (require.main === module) {
65
86
  startServer();
@@ -3,14 +3,16 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.isSameOrigin = isSameOrigin;
5
5
  exports.tryParseUrl = tryParseUrl;
6
- const url_1 = require("url");
6
+ const node_url_1 = require("node:url");
7
7
  const logger_1 = require("../config/logger");
8
8
  function isSameOrigin(thisUrl, thatUrl) {
9
9
  logger_1.logger.debug(`Comparing origins: ${thisUrl?.origin} and ${thatUrl?.origin}`);
10
- if (!thisUrl || !thatUrl)
10
+ if (!thisUrl || !thatUrl) {
11
11
  return false;
12
- if (thisUrl.origin === thatUrl.origin)
12
+ }
13
+ if (thisUrl.origin === thatUrl.origin) {
13
14
  return true;
15
+ }
14
16
  const isSameProtocol = thisUrl.protocol === thatUrl.protocol;
15
17
  const isSameHost = thisUrl.hostname === thatUrl.hostname;
16
18
  const isSamePort = thisUrl.port === thatUrl.port;
@@ -22,7 +24,7 @@ function tryParseUrl(urlString) {
22
24
  return undefined;
23
25
  }
24
26
  try {
25
- return new url_1.URL(urlString);
27
+ return new node_url_1.URL(urlString);
26
28
  }
27
29
  catch (error) {
28
30
  logger_1.logger.warn(`Invalid URL string: ${urlString} (${error})`);
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "info": {
34
34
  "title": "beavuck-time",
35
- "version": "2.3.13",
35
+ "version": "2.4.1",
36
36
  "description": "Get time in ISO format, in UTC timezone, from a simple, lightweight node server",
37
37
  "license": {
38
38
  "name": "Unlicense"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beavuck-time",
3
- "version": "2.3.13",
3
+ "version": "2.4.1",
4
4
  "description": "Get time in ISO format, in UTC timezone, from a simple, lightweight node server",
5
5
  "keywords": [
6
6
  "time",
@@ -46,7 +46,7 @@
46
46
  "test": "DOTENV_CONFIG_PATH=./.env.test jest --coverage",
47
47
  "build": "tsoa spec-and-routes && tsc",
48
48
  "start": "node build/server.js",
49
- "go": "rm -rf build && npm install && npm run build && npm run clean && npm run test && npm start"
49
+ "go": "rm -rf build && npm install && npm run build && npm run clean && npm run test && npm run start"
50
50
  },
51
51
  "dependencies": {
52
52
  "@dotenvx/dotenvx": "^1.51.0",