beavuck-time 2.2.0 → 2.3.0
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/.env +7 -7
- package/.env.test +7 -7
- package/DOCKERHUB_OVERVIEW.md +26 -26
- package/README.md +69 -30
- package/openapi/swagger.json +1 -1
- package/package.json +4 -1
- package/src/__tests__/api.test.ts +7 -7
- package/src/config/corsOptions.ts +2 -2
- package/src/config/logger.ts +3 -3
- package/src/middlewares/corsMiddleware.ts +1 -1
- package/src/middlewares/rateLimiter.ts +1 -1
- package/src/server.ts +59 -29
- package/tsconfig.json +1 -1
package/.env
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# CORS
|
|
2
2
|
## URL of this API
|
|
3
|
-
|
|
3
|
+
BEAVUCK_TIME_HOST_URL=http://localhost:3000
|
|
4
4
|
## To allow requests from any origin, include * (not recommended)
|
|
5
|
-
|
|
5
|
+
BEAVUCK_TIME_TRUSTED_ORIGINS=http://localhost:8477
|
|
6
6
|
|
|
7
7
|
# Server
|
|
8
8
|
## Port of this API: in a container, this is the internal port -- outside of a container, this is the external port. Default is 3000
|
|
9
|
-
|
|
9
|
+
BEAVUCK_TIME_API_PORT=3000
|
|
10
10
|
|
|
11
11
|
# Rate limiting
|
|
12
12
|
## Requests per minute for each IP address. If not a strictly positive, "no limit". Default is -1
|
|
13
|
-
|
|
13
|
+
BEAVUCK_TIME_RATE_LIMIT=-1
|
|
14
14
|
|
|
15
15
|
# Logging
|
|
16
16
|
## Level of detail in logs: error, warn, info, http, verbose, debug, silly. Default is info
|
|
17
|
-
|
|
17
|
+
BEAVUCK_TIME_LOG_LEVEL=info
|
|
18
18
|
## Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
|
|
19
|
-
|
|
19
|
+
BEAVUCK_TIME_MAX_LOG_FILES=64
|
|
20
20
|
## Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
|
|
21
|
-
|
|
21
|
+
BEAVUCK_TIME_MAX_SIZE_LOG_FILES=1m
|
package/.env.test
CHANGED
|
@@ -9,22 +9,22 @@ DOTENV_PUBLIC_KEY_TEST="02d5c6b7efe5269d1fd3626adac3338f0971e58e2555e8e27c53a7e7
|
|
|
9
9
|
|
|
10
10
|
# CORS
|
|
11
11
|
## URL of this API
|
|
12
|
-
|
|
12
|
+
BEAVUCK_TIME_HOST_URL=encrypted:BG5D1BHPO/UlcKh8c1s4s2YDqFQ0yIiduOKDHAsc9GNlm4/BRSPwxHNka+jJopZp++w+nfARNbSJwdgBoZvlbjujb/E8uJ2CX4erxmh1j+HBpmri2ocBa04NnJodMojPkcuxJqP3RSKfh61u+Fo1oTpCoZ8yYg==
|
|
13
13
|
## To allow requests from any origin, include * (not recommended)
|
|
14
|
-
|
|
14
|
+
BEAVUCK_TIME_TRUSTED_ORIGINS=encrypted:BE1fOSnJ8rnMU+A1orA9/Ujee4ypigUywnZ9g85l74XiNxEDoOdQmAIiSjYut+y/Y5b3VkqF+vBAgP++LYCMGTPoANWHgscZ+DAzsgqeFjsFp9WTKuWXxTeauI3HzLWI3v44o67oSZ1myWk9wsvc9WWwN4plrA==
|
|
15
15
|
|
|
16
16
|
# Server
|
|
17
17
|
## Port of this API: in a container, this is the internal port -- outside of a container, this is the external port. Default is 3000
|
|
18
|
-
|
|
18
|
+
BEAVUCK_TIME_API_PORT=encrypted:BGw4+UyXhxyfMzdxNjZBzTN/akEDL77i+XDqNRZA6w9ed7GYrQyYvuvEho+4VYir9T014sgLYRNeS09cR2mJV8pjyYWzHYhdXQyDeihUcNbLvIpfsZu7KH+aIeHaPx5YIRGJDSM=
|
|
19
19
|
|
|
20
20
|
# Rate limiting
|
|
21
21
|
## Requests per minute for each IP address. If not a strictly positive, "no limit". Default is -1
|
|
22
|
-
|
|
22
|
+
BEAVUCK_TIME_RATE_LIMIT=encrypted:BD1uTBsjTAjd+GYlZGM0S5DVCkmiOSEouDmLQj9XztlHpP2u8KyHxByIzUed8qkJXzbcf8zvTsANzY44V91m9UZZqXeZDOQlCqmn+ZpzmPDSA3VMUPzIdUpYF9DD7rWIgxvF
|
|
23
23
|
|
|
24
24
|
# Logging
|
|
25
25
|
## Level of detail in logs: error, warn, info, http, verbose, debug, silly. Default is info
|
|
26
|
-
|
|
26
|
+
BEAVUCK_TIME_LOG_LEVEL=encrypted:BGfqcxa9XJrMBk7U9QqkbfnXimot5dnz0kRroclEwtsY0JVc8VCw35idcSKpdS3jhCIxg0enNCZEbVMFMt6Q37yvYlIUqCKqk229moGTDSkQ2V/38pwnmzMS1LD/+nUH2yyFpWA=
|
|
27
27
|
## Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
|
|
28
|
-
|
|
28
|
+
BEAVUCK_TIME_MAX_LOG_FILES=encrypted:BET35n9Sfhqkdahr+JCwV/1sfX5zCE2FmH4lFFOi/WUhg9Wi+DIm2NH2WqfHcYIiWcRRnqpr3f2gnXwLsmRMcSydk7gxuERgA+mn0F2ygzbjTQUw+6l3snLgHKskKTuuA1rO
|
|
29
29
|
## Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
|
|
30
|
-
|
|
30
|
+
BEAVUCK_TIME_MAX_SIZE_LOG_FILES=encrypted:BJLXlbtMDxs5di0o+FRr6Im9gYs3GayVwjWomavHzCychYr9JZyOyMTrBN8YrusYoHklvEoHuGfIkUB1m8ErDEl8eJlJvaPZr30+NLPujWSrVwb1B9v8lBon2TfrDQulqSX6
|
package/DOCKERHUB_OVERVIEW.md
CHANGED
|
@@ -83,20 +83,20 @@ time:
|
|
|
83
83
|
- 'SOME_PORT_NUMBER:3000'
|
|
84
84
|
# HOST_PORT:CONTAINER_PORT (Since we are in a container, CONTAINER_PORT corresponds to the API_PORT variable below)
|
|
85
85
|
environment:
|
|
86
|
-
-
|
|
87
|
-
#
|
|
88
|
-
-
|
|
89
|
-
#
|
|
90
|
-
-
|
|
91
|
-
#
|
|
92
|
-
-
|
|
93
|
-
#
|
|
94
|
-
-
|
|
95
|
-
#
|
|
96
|
-
-
|
|
97
|
-
#
|
|
98
|
-
-
|
|
99
|
-
#
|
|
86
|
+
- BEAVUCK_TIME_HOST_URL: https://time-api.example.com
|
|
87
|
+
# BEAVUCK_TIME_HOST_URL: That API's URL. Essential for CORS config.
|
|
88
|
+
- BEAVUCK_TIME_TRUSTED_ORIGINS: https://my.app.com,https://my-other.app.com
|
|
89
|
+
# 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
|
|
90
|
+
- BEAVUCK_TIME_API_PORT: 3000
|
|
91
|
+
# BEAVUCK_TIME_API_PORT: Optional. Internal port when in a container. Defaults to 3000
|
|
92
|
+
- BEAVUCK_TIME_RATE_LIMIT: 100
|
|
93
|
+
# 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
|
|
94
|
+
- BEAVUCK_TIME_LOG_LEVEL: info
|
|
95
|
+
# BEAVUCK_TIME_LOG_LEVEL: Optional. Logging levels include error, warn, info, http, verbose, debug, silly. Defaults to info
|
|
96
|
+
- BEAVUCK_TIME_MAX_LOG_FILES: 64
|
|
97
|
+
# BEAVUCK_TIME_MAX_LOG_FILES: Optional. Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
|
|
98
|
+
- BEAVUCK_TIME_MAX_SIZE_LOG_FILES: 1m
|
|
99
|
+
# BEAVUCK_TIME_MAX_SIZE_LOG_FILES: Optional. Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
Here's the simple docker compose file I used to test this service locally:
|
|
@@ -108,9 +108,9 @@ services:
|
|
|
108
108
|
ports:
|
|
109
109
|
- '3000:3000'
|
|
110
110
|
environment:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
BEAVUCK_TIME_HOST_URL: http://127.0.0.1:3000
|
|
112
|
+
BEAVUCK_TIME_TRUSTED_ORIGINS: http://127.0.0.1:8000,http://localhost:8000
|
|
113
|
+
BEAVUCK_TIME_LOG_LEVEL: debug
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
When you're ready, just run your services with:
|
|
@@ -145,15 +145,15 @@ variable.
|
|
|
145
145
|
|
|
146
146
|
When you use the API, keep in mind what roles these headers play:
|
|
147
147
|
|
|
148
|
-
| `"Origin:"`
|
|
149
|
-
|
|
|
150
|
-
| Defined and API `
|
|
151
|
-
| Trusted
|
|
152
|
-
| Same as this API's host
|
|
153
|
-
| Not defined
|
|
154
|
-
| Defined and not trusted
|
|
155
|
-
| Not defined
|
|
156
|
-
| Not defined or not trusted
|
|
148
|
+
| `"Origin:"` | `"Referrer:"`* | Result |
|
|
149
|
+
| --------------------------------------------------------- | ------------------------------ | ------ |
|
|
150
|
+
| Defined and API `BEAVUCK_TIME_TRUSTED_ORIGINS` set to `*` | Whatever | ✅ |
|
|
151
|
+
| Trusted | Whatever | ✅ |
|
|
152
|
+
| Same as this API's host | Whatever | ✅ |
|
|
153
|
+
| Not defined | Same as this API's host | ✅ |
|
|
154
|
+
| Defined and not trusted | Whatever | 🛑 |
|
|
155
|
+
| Not defined | Not defined | 🛑 |
|
|
156
|
+
| Not defined or not trusted | Different from this API's host | 🛑 |
|
|
157
157
|
* AKA `Referer` (sic).
|
|
158
158
|
---
|
|
159
159
|
|
package/README.md
CHANGED
|
@@ -49,14 +49,15 @@ Dockerized for easy deployment and scaling.
|
|
|
49
49
|
## 🔍 Where
|
|
50
50
|
|
|
51
51
|
The code lives on [GitLab](https://gitlab.com/beavuck-services/time),
|
|
52
|
-
|
|
52
|
+
the Docker image is hosted on [Docker Hub](https://hub.docker.com/r/beavuck/time),
|
|
53
|
+
and it's also stored as a package on [NPM](https://www.npmjs.com/package/beavuck-time)
|
|
53
54
|
|
|
54
55
|
### 🦊 GitLab
|
|
55
56
|
|
|
56
|
-
You can find the code on GitLab, where, once you have read the [CONTRIBUTING.md](CONTRIBUTING.md) file, you can also
|
|
57
|
+
You can find the code on GitLab, where, once you have read the [CONTRIBUTING.md](https://gitlab.com/beavuck-services/time/-/blob/main/CONTRIBUTING.md?ref_type=heads) file, you can also
|
|
57
58
|
create issues and merge requests.
|
|
58
59
|
|
|
59
|
-
Feel free to fork the repo and make your own changes at will, as per the [UNLICENSE](UNLICENSE).
|
|
60
|
+
Feel free to fork the repo and make your own changes at will, as per the [UNLICENSE](https://gitlab.com/beavuck-services/time/-/blob/main/UNLICENSE?ref_type=heads).
|
|
60
61
|
|
|
61
62
|
### 🐳 Docker Hub
|
|
62
63
|
|
|
@@ -68,12 +69,50 @@ When the time comes to go to production, to protect yourself from surprise break
|
|
|
68
69
|
specific minor version tags, such as `beavuck/time:2.0` : those will not get breaking changes, but they will get
|
|
69
70
|
security updates and bug fixes while they're active.
|
|
70
71
|
|
|
72
|
+
### Ⓝ NPM
|
|
73
|
+
|
|
74
|
+
You can also use this service directly via npm:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install beavuck-time
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Programmatically
|
|
81
|
+
|
|
82
|
+
Then, if you want to run this service programmatically in your Node.js project:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { startServer } from 'beavuck-time'
|
|
86
|
+
|
|
87
|
+
startServer({
|
|
88
|
+
port: 3000,
|
|
89
|
+
hostUrl: 'https://time-api.example.com',
|
|
90
|
+
trustedOrigins: 'https://my.app.com,https://my-other.app.com'
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
You can rely on environment variables (BEAVUCK_TIME_HOST_URL, etc.) instead of passing an options object. Refer to
|
|
95
|
+
the docker Compose example below to see an exhaustive list of available environment variables and what they do.
|
|
96
|
+
|
|
97
|
+
#### CLI
|
|
98
|
+
|
|
99
|
+
To run this service directly in your command line interface, just run:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx beavuck-time
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Refer to the docker Compose example below to see an exhaustive list of available environment variables and what they do.
|
|
106
|
+
|
|
71
107
|
---
|
|
72
108
|
|
|
73
109
|
## ⚙️ Usage
|
|
74
110
|
|
|
75
111
|
### 🪧 Set up (docker-compose example)
|
|
76
112
|
|
|
113
|
+
(The section below supposes you're using a Docker image. If using the npm package, you can set those environment variable
|
|
114
|
+
on the host directly, instead of doing so in the container like detailed below)
|
|
115
|
+
|
|
77
116
|
To run the service in a docker-compose environment, add this in your `docker-compose.yml`'s services section:
|
|
78
117
|
|
|
79
118
|
```yaml
|
|
@@ -83,20 +122,20 @@ time:
|
|
|
83
122
|
- 'SOME_PORT_NUMBER:3000'
|
|
84
123
|
# HOST_PORT:CONTAINER_PORT (Since we are in a container, CONTAINER_PORT corresponds to the API_PORT variable below)
|
|
85
124
|
environment:
|
|
86
|
-
-
|
|
87
|
-
#
|
|
88
|
-
-
|
|
89
|
-
#
|
|
90
|
-
-
|
|
91
|
-
#
|
|
92
|
-
-
|
|
93
|
-
#
|
|
94
|
-
-
|
|
95
|
-
#
|
|
96
|
-
-
|
|
97
|
-
#
|
|
98
|
-
-
|
|
99
|
-
#
|
|
125
|
+
- BEAVUCK_TIME_HOST_URL: https://time-api.example.com
|
|
126
|
+
# BEAVUCK_TIME_HOST_URL: That API's URL. Essential for CORS config.
|
|
127
|
+
- BEAVUCK_TIME_TRUSTED_ORIGINS: https://my.app.com,https://my-other.app.com
|
|
128
|
+
# 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
|
|
129
|
+
- BEAVUCK_TIME_API_PORT: 3000
|
|
130
|
+
# BEAVUCK_TIME_API_PORT: Optional. Internal port when in a container. Defaults to 3000
|
|
131
|
+
- BEAVUCK_TIME_RATE_LIMIT: 100
|
|
132
|
+
# 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
|
|
133
|
+
- BEAVUCK_TIME_LOG_LEVEL: info
|
|
134
|
+
# BEAVUCK_TIME_LOG_LEVEL: Optional. Logging levels include error, warn, info, http, verbose, debug, silly. Defaults to info
|
|
135
|
+
- BEAVUCK_TIME_MAX_LOG_FILES: 64
|
|
136
|
+
# BEAVUCK_TIME_MAX_LOG_FILES: Optional. Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
|
|
137
|
+
- BEAVUCK_TIME_MAX_SIZE_LOG_FILES: 1m
|
|
138
|
+
# BEAVUCK_TIME_MAX_SIZE_LOG_FILES: Optional. Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
|
|
100
139
|
```
|
|
101
140
|
|
|
102
141
|
Here's the simple docker compose file I used to test this service locally:
|
|
@@ -108,9 +147,9 @@ services:
|
|
|
108
147
|
ports:
|
|
109
148
|
- '3000:3000'
|
|
110
149
|
environment:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
150
|
+
BEAVUCK_TIME_HOST_URL: http://127.0.0.1:3000
|
|
151
|
+
BEAVUCK_TIME_TRUSTED_ORIGINS: http://127.0.0.1:8000,http://localhost:8000
|
|
152
|
+
BEAVUCK_TIME_LOG_LEVEL: debug
|
|
114
153
|
```
|
|
115
154
|
|
|
116
155
|
When you're ready, just run your services with:
|
|
@@ -145,15 +184,15 @@ variable.
|
|
|
145
184
|
|
|
146
185
|
When you use the API, keep in mind what roles these headers play:
|
|
147
186
|
|
|
148
|
-
| `"Origin:"`
|
|
149
|
-
|
|
|
150
|
-
| Defined and API `
|
|
151
|
-
| Trusted
|
|
152
|
-
| Same as this API's host
|
|
153
|
-
| Not defined
|
|
154
|
-
| Defined and not trusted
|
|
155
|
-
| Not defined
|
|
156
|
-
| Not defined or not trusted
|
|
187
|
+
| `"Origin:"` | `"Referrer:"`* | Result |
|
|
188
|
+
| --------------------------------------------------------- | ------------------------------ | ------ |
|
|
189
|
+
| Defined and API `BEAVUCK_TIME_TRUSTED_ORIGINS` set to `*` | Whatever | ✅ |
|
|
190
|
+
| Trusted | Whatever | ✅ |
|
|
191
|
+
| Same as this API's host | Whatever | ✅ |
|
|
192
|
+
| Not defined | Same as this API's host | ✅ |
|
|
193
|
+
| Defined and not trusted | Whatever | 🛑 |
|
|
194
|
+
| Not defined | Not defined | 🛑 |
|
|
195
|
+
| Not defined or not trusted | Different from this API's host | 🛑 |
|
|
157
196
|
|
|
158
197
|
*AKA `Referer` (sic).
|
|
159
198
|
|
|
@@ -212,4 +251,4 @@ sequenceDiagram
|
|
|
212
251
|
|
|
213
252
|
Have at it.
|
|
214
253
|
|
|
215
|
-
This project uses the Unlicense. See the [UNLICENSE](UNLICENSE) file for details.
|
|
254
|
+
This project uses the Unlicense. See the [UNLICENSE](https://gitlab.com/beavuck-services/time/-/blob/main/UNLICENSE?ref_type=heads) file for details.
|
package/openapi/swagger.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beavuck-time",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Get time in ISO format, in UTC timezone, from a simple, lightweight node server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"time",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"author": "Alexis Drai",
|
|
22
22
|
"type": "commonjs",
|
|
23
23
|
"main": "build/api.js",
|
|
24
|
+
"bin": {
|
|
25
|
+
"beavuck-time": "./build/server.js"
|
|
26
|
+
},
|
|
24
27
|
"scripts": {
|
|
25
28
|
"run-integration-tests": "cd api-tests/bruno && npm install -g @usebruno/cli && bru run --env local --bail",
|
|
26
29
|
"up-patch": "npm version patch --no-git-tag-version",
|
|
@@ -6,7 +6,7 @@ import {RFC_3339_FORMAT} from '../types/isoTimestamp'
|
|
|
6
6
|
let callsMadeSoFar = 0
|
|
7
7
|
|
|
8
8
|
function getSomeTrustedOrigin(): string {
|
|
9
|
-
return process.env.
|
|
9
|
+
return process.env.BEAVUCK_TIME_TRUSTED_ORIGINS?.split(',')[0] ?? ''
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -44,7 +44,7 @@ describe('App Initialization', () => {
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
it('should allow requests with no origin header but a referrer header identical to server url', async () => {
|
|
47
|
-
const REFERRER: string = process.env.
|
|
47
|
+
const REFERRER: string = process.env.BEAVUCK_TIME_HOST_URL!
|
|
48
48
|
const res = await request(api).get('/now').set('Referrer', REFERRER)
|
|
49
49
|
expect(res.header['access-control-allow-origin']).toBeUndefined()
|
|
50
50
|
expect(res.status).toBe(StatusCodes.OK)
|
|
@@ -52,7 +52,7 @@ describe('App Initialization', () => {
|
|
|
52
52
|
})
|
|
53
53
|
|
|
54
54
|
it('should allow requests with no origin header but a referer (sic) header identical to server url', async () => {
|
|
55
|
-
const REFERER: string = process.env.
|
|
55
|
+
const REFERER: string = process.env.BEAVUCK_TIME_HOST_URL!
|
|
56
56
|
const res = await request(api).get('/now').set('Referer', REFERER)
|
|
57
57
|
expect(res.header['access-control-allow-origin']).toBeUndefined()
|
|
58
58
|
expect(res.status).toBe(StatusCodes.OK)
|
|
@@ -72,13 +72,13 @@ describe('App Initialization', () => {
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
it('should return 500 if HOST_URL is not set', async () => {
|
|
75
|
-
const originalHostUrl = process.env.
|
|
76
|
-
delete process.env.
|
|
75
|
+
const originalHostUrl = process.env.BEAVUCK_TIME_HOST_URL
|
|
76
|
+
delete process.env.BEAVUCK_TIME_HOST_URL
|
|
77
77
|
|
|
78
78
|
const res = await request(api).get('/now')
|
|
79
79
|
expect(res.status).toBe(StatusCodes.INTERNAL_SERVER_ERROR)
|
|
80
80
|
|
|
81
|
-
process.env.
|
|
81
|
+
process.env.BEAVUCK_TIME_HOST_URL = originalHostUrl
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
it('should block requests with non-trusted referrer header if origin is not OK', async () => {
|
|
@@ -87,7 +87,7 @@ describe('App Initialization', () => {
|
|
|
87
87
|
})
|
|
88
88
|
|
|
89
89
|
it('should block requests that exceed the rate limit', async () => {
|
|
90
|
-
const RATE_LIMIT = parseInt(process.env.
|
|
90
|
+
const RATE_LIMIT = parseInt(process.env.BEAVUCK_TIME_RATE_LIMIT!, 10)
|
|
91
91
|
while (callsMadeSoFar <= RATE_LIMIT) {
|
|
92
92
|
const {res} = await makeSuccessfulCallToNowEndpoint()
|
|
93
93
|
if (callsMadeSoFar > RATE_LIMIT) {
|
|
@@ -39,7 +39,7 @@ export function getHostUrl(): URL | undefined {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function initHostUrl(): void {
|
|
42
|
-
HOST_URL = new URL(process.env.
|
|
42
|
+
HOST_URL = new URL(process.env.BEAVUCK_TIME_HOST_URL!)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function getTrustedOrigins(): string[] {
|
|
@@ -50,7 +50,7 @@ export function getTrustedOrigins(): string[] {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function initTrustedOrigins(): void {
|
|
53
|
-
const trustedOrigins: string = process.env.
|
|
53
|
+
const trustedOrigins: string = process.env.BEAVUCK_TIME_TRUSTED_ORIGINS!
|
|
54
54
|
TRUSTED_ORIGINS = trustedOrigins.split(',')
|
|
55
55
|
}
|
|
56
56
|
|
package/src/config/logger.ts
CHANGED
|
@@ -7,9 +7,9 @@ import dotenvx from '@dotenvx/dotenvx'
|
|
|
7
7
|
dotenvx.config()
|
|
8
8
|
|
|
9
9
|
const LOGS_DIR = 'logs'
|
|
10
|
-
const LOG_LEVEL: string = process.env.
|
|
11
|
-
const MAX_LOG_FILES: string | number = process.env.
|
|
12
|
-
const MAX_SIZE_LOG_FILES: string = process.env.
|
|
10
|
+
const LOG_LEVEL: string = process.env.BEAVUCK_TIME_LOG_LEVEL ?? 'info'
|
|
11
|
+
const MAX_LOG_FILES: string | number = process.env.BEAVUCK_TIME_MAX_LOG_FILES ?? 64
|
|
12
|
+
const MAX_SIZE_LOG_FILES: string = process.env.BEAVUCK_TIME_MAX_SIZE_LOG_FILES ?? '1m'
|
|
13
13
|
|
|
14
14
|
const dailyRotateFileTransport = new transports.DailyRotateFile({
|
|
15
15
|
filename: `${LOGS_DIR}/%DATE%-combined.log`,
|
|
@@ -13,7 +13,7 @@ export const corsMiddleware = (
|
|
|
13
13
|
res: express.Response,
|
|
14
14
|
next: express.NextFunction,
|
|
15
15
|
) => {
|
|
16
|
-
const HOST_URL = tryParseUrl(process.env.
|
|
16
|
+
const HOST_URL = tryParseUrl(process.env.BEAVUCK_TIME_HOST_URL ?? '')
|
|
17
17
|
// 'referer' is a misspelling that was kept for compatibility: https://en.wikipedia.org/wiki/HTTP_referer
|
|
18
18
|
const referrerHeader: string | undefined =
|
|
19
19
|
(req.headers.referrer as string) || req.headers.referer
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import rateLimit from 'express-rate-limit'
|
|
4
4
|
|
|
5
5
|
const NO_RATE_LIMIT = '-1'
|
|
6
|
-
const RATE_LIMIT = parseInt(process.env.
|
|
6
|
+
const RATE_LIMIT = parseInt(process.env.BEAVUCK_TIME_RATE_LIMIT ?? NO_RATE_LIMIT, 10)
|
|
7
7
|
|
|
8
8
|
const SECONDS_IN_ONE_MINUTE = 60
|
|
9
9
|
const MILLISECONDS_IN_ONE_SECOND = 1000
|
package/src/server.ts
CHANGED
|
@@ -1,53 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
// src/server.ts
|
|
2
4
|
|
|
3
5
|
import {api} from './api'
|
|
4
6
|
import {logger} from './config/logger'
|
|
5
7
|
import {tryParseUrl} from './utils/urlUtil'
|
|
6
8
|
import dotenvx from '@dotenvx/dotenvx'
|
|
9
|
+
import http from 'http'
|
|
7
10
|
|
|
8
11
|
dotenvx.config()
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
type ServerOptions = {
|
|
14
|
+
port?: number
|
|
15
|
+
hostUrl?: string
|
|
16
|
+
trustedOrigins?: string
|
|
17
|
+
}
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
function validateEnv({
|
|
20
|
+
hostUrl,
|
|
21
|
+
trustedOrigins,
|
|
22
|
+
}: Required<Pick<ServerOptions, 'hostUrl' | 'trustedOrigins'>>) {
|
|
23
|
+
if (!hostUrl || !tryParseUrl(hostUrl)) {
|
|
24
|
+
logger.error('HOST_URL not set or is not a valid URL')
|
|
25
|
+
process.exit(1)
|
|
26
|
+
}
|
|
17
27
|
|
|
18
|
-
if (!
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
if (!trustedOrigins) {
|
|
29
|
+
logger.error('TRUSTED_ORIGINS not set')
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
process.
|
|
26
|
-
|
|
34
|
+
export function startServer(options: ServerOptions = {}): http.Server {
|
|
35
|
+
const API_PORT = options.port ?? process.env.BEAVUCK_TIME_API_PORT ?? 3000
|
|
36
|
+
const HOST_URL = options.hostUrl ?? process.env.BEAVUCK_TIME_HOST_URL ?? ''
|
|
37
|
+
const TRUSTED_ORIGINS =
|
|
38
|
+
options.trustedOrigins ?? process.env.BEAVUCK_TIME_TRUSTED_ORIGINS ?? ''
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
validateEnv({hostUrl: HOST_URL, trustedOrigins: TRUSTED_ORIGINS})
|
|
41
|
+
|
|
42
|
+
const server = api.listen(API_PORT, () => {
|
|
43
|
+
logger.info(
|
|
44
|
+
`
|
|
31
45
|
| | | | ,=.
|
|
32
46
|
| |__ ___ __ ___ ___ _ ___| | __ ,=""""==.__.=" o".___
|
|
33
47
|
| '_ \\ / _ \\/ _\` \\ \\ / / | | |/ __| |/ / ,=.==" ___/
|
|
34
48
|
| |_) | __/ (_| |\\ V /| |_| | (__| < ,==.," , , \\,===""
|
|
35
49
|
|_.__/ \\___|\\__,_| \\_/ \\__,_|\\___|_|\\_\\ < ,==) \\"'"=._.==) \\
|
|
36
50
|
\`=='' \`" \`"
|
|
37
|
-
|
|
51
|
+
|
|
38
52
|
Beavuck Time microservice started successfully
|
|
39
|
-
|
|
40
|
-
Ready on API port ${API_PORT} (if this is running in a container, this port number is internal to the container)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const gracefulShutdown = (signal: string) => {
|
|
45
|
-
logger.info(`${signal} signal received: closing HTTP server`)
|
|
46
|
-
server.close(() => {
|
|
47
|
-
logger.info('HTTP server closed')
|
|
48
|
-
process.exit(0)
|
|
53
|
+
|
|
54
|
+
Ready on API port ${API_PORT} (if this is running in a container, this port number is internal to the container)
|
|
55
|
+
`,
|
|
56
|
+
)
|
|
49
57
|
})
|
|
58
|
+
|
|
59
|
+
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
60
|
+
logger.error(`Beavuck Time Server error [${err.code}]: ${err.message}`)
|
|
61
|
+
process.exit(1)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const gracefulShutdown = (signal: string) => {
|
|
65
|
+
logger.info(`${signal} signal received: closing HTTP server`)
|
|
66
|
+
server.close(() => {
|
|
67
|
+
logger.info('HTTP server closed')
|
|
68
|
+
process.exit(0)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const SIGTERM = 'SIGTERM'
|
|
73
|
+
const SIGINT = 'SIGINT'
|
|
74
|
+
process.on(SIGTERM, () => gracefulShutdown(SIGTERM))
|
|
75
|
+
process.on(SIGINT, () => gracefulShutdown(SIGINT))
|
|
76
|
+
|
|
77
|
+
return server
|
|
50
78
|
}
|
|
51
79
|
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
// Support running as standalone binary
|
|
81
|
+
if (require.main === module) {
|
|
82
|
+
startServer()
|
|
83
|
+
}
|