@into-cps-association/logger-ms 0.1.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/LICENSE.md +3 -0
- package/README.md +198 -0
- package/dist/src/app.controller.d.ts +12 -0
- package/dist/src/app.controller.js +53 -0
- package/dist/src/app.controller.js.map +1 -0
- package/dist/src/app.module.d.ts +2 -0
- package/dist/src/app.module.js +44 -0
- package/dist/src/app.module.js.map +1 -0
- package/dist/src/config/certificates.d.ts +5 -0
- package/dist/src/config/certificates.js +47 -0
- package/dist/src/config/certificates.js.map +1 -0
- package/dist/src/config/cli.d.ts +1 -0
- package/dist/src/config/cli.js +45 -0
- package/dist/src/config/cli.js.map +1 -0
- package/dist/src/config/config.interface.d.ts +17 -0
- package/dist/src/config/config.interface.js +2 -0
- package/dist/src/config/config.interface.js.map +1 -0
- package/dist/src/config/config.service.d.ts +21 -0
- package/dist/src/config/config.service.js +275 -0
- package/dist/src/config/config.service.js.map +1 -0
- package/dist/src/config/cors.d.ts +7 -0
- package/dist/src/config/cors.js +29 -0
- package/dist/src/config/cors.js.map +1 -0
- package/dist/src/config/util.d.ts +1 -0
- package/dist/src/config/util.js +5 -0
- package/dist/src/config/util.js.map +1 -0
- package/dist/src/dto/log-event.dto.d.ts +37 -0
- package/dist/src/dto/log-event.dto.js +125 -0
- package/dist/src/dto/log-event.dto.js.map +1 -0
- package/dist/src/log-event-validation.pipe.d.ts +4 -0
- package/dist/src/log-event-validation.pipe.js +23 -0
- package/dist/src/log-event-validation.pipe.js.map +1 -0
- package/dist/src/logger-auth.guard.d.ts +7 -0
- package/dist/src/logger-auth.guard.js +46 -0
- package/dist/src/logger-auth.guard.js.map +1 -0
- package/dist/src/logs/logs.module.d.ts +2 -0
- package/dist/src/logs/logs.module.js +19 -0
- package/dist/src/logs/logs.module.js.map +1 -0
- package/dist/src/logs/logs.service.d.ts +29 -0
- package/dist/src/logs/logs.service.js +201 -0
- package/dist/src/logs/logs.service.js.map +1 -0
- package/dist/src/main.d.ts +12 -0
- package/dist/src/main.js +59 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/socket-address-throttler.guard.d.ts +12 -0
- package/dist/src/socket-address-throttler.guard.js +10 -0
- package/dist/src/socket-address-throttler.guard.js.map +1 -0
- package/dist/src/validation.pipe.d.ts +8 -0
- package/dist/src/validation.pipe.js +20 -0
- package/dist/src/validation.pipe.js.map +1 -0
- package/dist/test/e2e/app.e2e.spec.d.ts +1 -0
- package/dist/test/e2e/app.e2e.spec.js +276 -0
- package/dist/test/e2e/app.e2e.spec.js.map +1 -0
- package/dist/test/unit/certificates.spec.d.ts +1 -0
- package/dist/test/unit/certificates.spec.js +46 -0
- package/dist/test/unit/certificates.spec.js.map +1 -0
- package/dist/test/unit/cli.spec.d.ts +1 -0
- package/dist/test/unit/cli.spec.js +100 -0
- package/dist/test/unit/cli.spec.js.map +1 -0
- package/dist/test/unit/config.service.spec.d.ts +1 -0
- package/dist/test/unit/config.service.spec.js +189 -0
- package/dist/test/unit/config.service.spec.js.map +1 -0
- package/dist/test/unit/cors.spec.d.ts +1 -0
- package/dist/test/unit/cors.spec.js +41 -0
- package/dist/test/unit/cors.spec.js.map +1 -0
- package/dist/test/unit/logger-auth.guard.spec.d.ts +1 -0
- package/dist/test/unit/logger-auth.guard.spec.js +40 -0
- package/dist/test/unit/logger-auth.guard.spec.js.map +1 -0
- package/dist/test/unit/logs.service.spec.d.ts +1 -0
- package/dist/test/unit/logs.service.spec.js +246 -0
- package/dist/test/unit/logs.service.spec.js.map +1 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.d.ts +1 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.js +18 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.js.map +1 -0
- package/dist/test/unit/validation.pipe.spec.d.ts +1 -0
- package/dist/test/unit/validation.pipe.spec.js +132 -0
- package/dist/test/unit/validation.pipe.spec.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +80 -0
package/LICENSE.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# :notebook: Logger microservice
|
|
2
|
+
|
|
3
|
+
The logger microservice ingests workflow log events produced by the DTaaS
|
|
4
|
+
client and stores them as JSON Lines (`.jsonl`), which is easy to process with
|
|
5
|
+
`jq`.
|
|
6
|
+
|
|
7
|
+
## :arrow_down: Install
|
|
8
|
+
|
|
9
|
+
### Default NPM Registry
|
|
10
|
+
|
|
11
|
+
The default registry for npm packages is [npmjs](https://registry.npmjs.org).
|
|
12
|
+
Install the package with the following command
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @into-cps-association/logger-ms
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Github NPM Registry
|
|
19
|
+
|
|
20
|
+
The package is also available in Github
|
|
21
|
+
[packages registry](https://github.com/orgs/INTO-CPS-Association/packages).
|
|
22
|
+
|
|
23
|
+
Set the registry and install the package with the following commands
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm config set @into-cps-association:registry https://npm.pkg.github.com
|
|
27
|
+
npm install -g @into-cps-association/logger-ms
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The _npm install_ command asks for username and password. The username is
|
|
31
|
+
your Github username and the password is your Github
|
|
32
|
+
[personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).
|
|
33
|
+
In order for the npm to download the package, your personal access token
|
|
34
|
+
needs to have _read:packages_ scope.
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
- `GET /logger/health` returns service status.
|
|
39
|
+
- `POST /logger` ingests one event and appends it to a JSONL file.
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
The service loads configuration in this order:
|
|
44
|
+
|
|
45
|
+
1. CLI `--config <file>` or `-c <file>`
|
|
46
|
+
2. `LOGGER_CONFIG_PATH` environment variable
|
|
47
|
+
3. `logger.yaml` in the current working directory (if present)
|
|
48
|
+
4. Built-in defaults
|
|
49
|
+
|
|
50
|
+
Environment variables always override YAML values.
|
|
51
|
+
|
|
52
|
+
### YAML fields
|
|
53
|
+
|
|
54
|
+
- `hostname` (default: `127.0.0.1`; set `0.0.0.0` for Docker/reverse-proxy
|
|
55
|
+
deployments)
|
|
56
|
+
- `port` (default: `4003`)
|
|
57
|
+
- `cors-allow-origin` (default: disabled; set to a single origin or YAML list
|
|
58
|
+
of origins for browser clients)
|
|
59
|
+
- `cors-allow-credentials` (default: `false`; enable only with explicit
|
|
60
|
+
allowed origins, never `*`)
|
|
61
|
+
- `auth-token` (default: empty string) — a static bearer token for non-browser
|
|
62
|
+
producers. When set, `POST /logger` requires an
|
|
63
|
+
`Authorization: Bearer <auth-token>` header matching this value; requests
|
|
64
|
+
without it are rejected with `401 Unauthorized`. Leave empty to accept
|
|
65
|
+
unauthenticated requests (the default). **Note:** the bundled DTaaS client
|
|
66
|
+
posts events via `navigator.sendBeacon`, which cannot set custom headers, so
|
|
67
|
+
enabling this setting will block ingestion from that client. Browser ingest
|
|
68
|
+
security is delegated to the reverse proxy.
|
|
69
|
+
- `tls` (default: `false`)
|
|
70
|
+
- `certs` (default: `./certs`)
|
|
71
|
+
- `log-file-path` (default: `./logs/workflow-logs.jsonl`)
|
|
72
|
+
- `max-payload-bytes` (default: `65536`)
|
|
73
|
+
- `log-max-bytes` (default: `52428800`)
|
|
74
|
+
- `log-retention-files` (default: `5` rotated files)
|
|
75
|
+
- `throttle-ttl` (default: `60000` milliseconds)
|
|
76
|
+
- `throttle-limit` (default: `120` requests per throttle window)
|
|
77
|
+
|
|
78
|
+
A sample configuration is given below. Copy it into a file named
|
|
79
|
+
`logger.yaml`, and update the values for your setup:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
hostname: 127.0.0.1
|
|
83
|
+
port: 4003
|
|
84
|
+
# Restrictive same origin.
|
|
85
|
+
cors-allow-origin: http://localhost
|
|
86
|
+
# One external domain.
|
|
87
|
+
# cors-allow-origin: https://client.example.org
|
|
88
|
+
# Three external domains.
|
|
89
|
+
# cors-allow-origin:
|
|
90
|
+
# - https://client-a.example.org
|
|
91
|
+
# - https://client-b.example.org
|
|
92
|
+
# - https://client-c.example.org
|
|
93
|
+
cors-allow-credentials: false
|
|
94
|
+
auth-token: ''
|
|
95
|
+
tls: false
|
|
96
|
+
certs: ./certs
|
|
97
|
+
log-file-path: ./logs/workflow-logs.jsonl
|
|
98
|
+
max-payload-bytes: 65536
|
|
99
|
+
log-max-bytes: 52428800
|
|
100
|
+
log-retention-files: 5
|
|
101
|
+
# Limits are per TCP peer; behind a reverse proxy this is a shared budget.
|
|
102
|
+
throttle-ttl: 60000
|
|
103
|
+
throttle-limit: 120
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The same sample can also be downloaded:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
curl -o logger.yaml \
|
|
110
|
+
https://raw.githubusercontent.com/INTO-CPS-Association/DTaaS/feature/distributed-demo/servers/logger/config/logger.yaml.sample
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Environment variables
|
|
114
|
+
|
|
115
|
+
- `LOGGER_CONFIG_PATH`
|
|
116
|
+
- `LOGGER_HOSTNAME` (default: `127.0.0.1`; use `0.0.0.0` in containers)
|
|
117
|
+
- `LOGGER_PORT` (default: `4003`)
|
|
118
|
+
- `LOGGER_CORS_ALLOW_ORIGIN` (default: disabled)
|
|
119
|
+
- `LOGGER_CORS_ALLOW_CREDENTIALS` (default: `false`)
|
|
120
|
+
- `LOGGER_AUTH_TOKEN`
|
|
121
|
+
- `LOGGER_TLS`
|
|
122
|
+
- `LOGGER_CERTS_DIR`
|
|
123
|
+
- `LOGGER_LOG_FILE_PATH` (default: `logs/workflow-logs.jsonl`)
|
|
124
|
+
- `LOGGER_MAX_PAYLOAD_BYTES` (default: `65536`)
|
|
125
|
+
- `LOGGER_LOG_MAX_BYTES` (default: `52428800`)
|
|
126
|
+
- `LOGGER_LOG_RETENTION_FILES` (default: `5`)
|
|
127
|
+
- `LOGGER_THROTTLE_TTL` (default: `60000` milliseconds)
|
|
128
|
+
- `LOGGER_THROTTLE_LIMIT` (default: `120`)
|
|
129
|
+
|
|
130
|
+
External authentication and edge rate limiting are provided by the reverse
|
|
131
|
+
proxy in the deployment compose files. The service also applies an internal
|
|
132
|
+
request throttle for callers that bypass the proxy.
|
|
133
|
+
|
|
134
|
+
The logger identifies callers by their TCP peer address and deliberately does
|
|
135
|
+
not trust forwarded client-address headers. Behind Traefik, requests therefore
|
|
136
|
+
share the proxy container's throttle budget. Set `throttle-limit` (or
|
|
137
|
+
`LOGGER_THROTTLE_LIMIT`) high enough for the combined request volume of all
|
|
138
|
+
expected users during `throttle-ttl`.
|
|
139
|
+
|
|
140
|
+
The service sends `Access-Control-Allow-Credentials: true` only when
|
|
141
|
+
`cors-allow-credentials` is enabled. Credentials cannot be enabled with a
|
|
142
|
+
wildcard CORS origin.
|
|
143
|
+
|
|
144
|
+
## TLS support
|
|
145
|
+
|
|
146
|
+
Set `tls: true` (or `LOGGER_TLS=true`) to enable HTTPS.
|
|
147
|
+
|
|
148
|
+
Certificates are read from the configured cert directory:
|
|
149
|
+
|
|
150
|
+
- `<certs>/fullchain.pem`
|
|
151
|
+
- `<certs>/privkey.pem`
|
|
152
|
+
|
|
153
|
+
If either file is missing and TLS is enabled, the service auto-generates a
|
|
154
|
+
self-signed certificate using OpenSSL with RSA-4096 + SHA-256 and stores the
|
|
155
|
+
files in the certs directory.
|
|
156
|
+
|
|
157
|
+
## Log storage format
|
|
158
|
+
|
|
159
|
+
Each request is appended as one JSON object per line (`.jsonl`). This format is
|
|
160
|
+
append-efficient and can be queried directly:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
jq -c '.event' logs/workflow-logs.jsonl
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The active log rotates at `log-max-bytes` and keeps the configured number of
|
|
167
|
+
rotated files. The defaults are 50 MiB and 5 rotated files, so capacity should
|
|
168
|
+
allow for the active file plus those retained rotated files.
|
|
169
|
+
|
|
170
|
+
Log writes are best-effort analytics storage. Graceful shutdown closes the
|
|
171
|
+
write stream, but recent events may be lost on hard container termination
|
|
172
|
+
because writes are not fsynced per request.
|
|
173
|
+
|
|
174
|
+
## :rocket: Use
|
|
175
|
+
|
|
176
|
+
Start the logger with the `logger.yaml` file in the working directory,
|
|
177
|
+
or with the built-in defaults if there is no such file:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
logger-ms
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
To use a configuration file at another location, run
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
logger-ms -c FILE-PATH
|
|
187
|
+
logger-ms --config FILE-PATH
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The logger listens on <http://127.0.0.1:4003/logger> by default.
|
|
191
|
+
You can press `Ctrl+C` to halt the application.
|
|
192
|
+
|
|
193
|
+
## Use in Docker Environment
|
|
194
|
+
|
|
195
|
+
The logger is also available as the `intocps/logger-ms` docker image.
|
|
196
|
+
Please see the
|
|
197
|
+
[docker instructions](https://github.com/INTO-CPS-Association/DTaaS/blob/feature/distributed-demo/servers/logger/DOCKER.md)
|
|
198
|
+
for running it.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import LogsService from './logs/logs.service.js';
|
|
2
|
+
import { LogEventDto } from './dto/log-event.dto.js';
|
|
3
|
+
type HealthResponse = {
|
|
4
|
+
status: 'ok';
|
|
5
|
+
};
|
|
6
|
+
export default class AppController {
|
|
7
|
+
private readonly logsService;
|
|
8
|
+
constructor(logsService: LogsService);
|
|
9
|
+
health(): HealthResponse;
|
|
10
|
+
ingestEvent(logEventDto: LogEventDto): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Body, Controller, Get, HttpStatus, Post, UseGuards, UsePipes, HttpCode, } from '@nestjs/common';
|
|
14
|
+
import { SkipThrottle } from '@nestjs/throttler';
|
|
15
|
+
import LogsService from './logs/logs.service.js';
|
|
16
|
+
import LogEventValidationPipe from './log-event-validation.pipe.js';
|
|
17
|
+
import LoggerAuthGuard from './logger-auth.guard.js';
|
|
18
|
+
let AppController = class AppController {
|
|
19
|
+
logsService;
|
|
20
|
+
constructor(logsService) {
|
|
21
|
+
this.logsService = logsService;
|
|
22
|
+
}
|
|
23
|
+
health() {
|
|
24
|
+
return { status: 'ok' };
|
|
25
|
+
}
|
|
26
|
+
async ingestEvent(logEventDto) {
|
|
27
|
+
await this.logsService.appendEvent(logEventDto);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
__decorate([
|
|
31
|
+
Get('health'),
|
|
32
|
+
HttpCode(HttpStatus.OK),
|
|
33
|
+
SkipThrottle(),
|
|
34
|
+
__metadata("design:type", Function),
|
|
35
|
+
__metadata("design:paramtypes", []),
|
|
36
|
+
__metadata("design:returntype", Object)
|
|
37
|
+
], AppController.prototype, "health", null);
|
|
38
|
+
__decorate([
|
|
39
|
+
Post(),
|
|
40
|
+
HttpCode(HttpStatus.NO_CONTENT),
|
|
41
|
+
UseGuards(LoggerAuthGuard),
|
|
42
|
+
UsePipes(LogEventValidationPipe),
|
|
43
|
+
__param(0, Body()),
|
|
44
|
+
__metadata("design:type", Function),
|
|
45
|
+
__metadata("design:paramtypes", [Object]),
|
|
46
|
+
__metadata("design:returntype", Promise)
|
|
47
|
+
], AppController.prototype, "ingestEvent", null);
|
|
48
|
+
AppController = __decorate([
|
|
49
|
+
Controller('logger'),
|
|
50
|
+
__metadata("design:paramtypes", [LogsService])
|
|
51
|
+
], AppController);
|
|
52
|
+
export default AppController;
|
|
53
|
+
//# sourceMappingURL=app.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EACL,IAAI,EACJ,UAAU,EACV,GAAG,EACH,UAAU,EACV,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAEjD,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,eAAe,MAAM,wBAAwB,CAAC;AAOtC,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEH;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAKzD,MAAM;QACJ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAMK,AAAN,KAAK,CAAC,WAAW,CAAS,WAAwB;QAChD,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;CACF,CAAA;AAXC;IAHC,GAAG,CAAC,QAAQ,CAAC;IACb,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;IACvB,YAAY,EAAE;;;;2CAGd;AAMK;IAJL,IAAI,EAAE;IACN,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC;IAC/B,SAAS,CAAC,eAAe,CAAC;IAC1B,QAAQ,CAAC,sBAAsB,CAAC;IACd,WAAA,IAAI,EAAE,CAAA;;;;gDAExB;AAjBkB,aAAa;IADjC,UAAU,CAAC,QAAQ,CAAC;qCAGuB,WAAW;GAFlC,aAAa,CAkBjC;eAlBoB,aAAa"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Module } from '@nestjs/common';
|
|
8
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
9
|
+
import { ThrottlerModule } from '@nestjs/throttler';
|
|
10
|
+
import AppController from './app.controller.js';
|
|
11
|
+
import LogEventValidationPipe from './log-event-validation.pipe.js';
|
|
12
|
+
import LoggerAuthGuard from './logger-auth.guard.js';
|
|
13
|
+
import Config from './config/config.service.js';
|
|
14
|
+
import LogsModule from './logs/logs.module.js';
|
|
15
|
+
import SocketAddressThrottlerGuard from './socket-address-throttler.guard.js';
|
|
16
|
+
let AppModule = class AppModule {
|
|
17
|
+
};
|
|
18
|
+
AppModule = __decorate([
|
|
19
|
+
Module({
|
|
20
|
+
imports: [
|
|
21
|
+
LogsModule,
|
|
22
|
+
ThrottlerModule.forRootAsync({
|
|
23
|
+
imports: [LogsModule],
|
|
24
|
+
inject: [Config],
|
|
25
|
+
useFactory: (config) => ({
|
|
26
|
+
throttlers: [
|
|
27
|
+
{
|
|
28
|
+
ttl: config.getThrottleTtl(),
|
|
29
|
+
limit: config.getThrottleLimit(),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}),
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
controllers: [AppController],
|
|
36
|
+
providers: [
|
|
37
|
+
LogEventValidationPipe,
|
|
38
|
+
LoggerAuthGuard,
|
|
39
|
+
{ provide: APP_GUARD, useClass: SocketAddressThrottlerGuard },
|
|
40
|
+
],
|
|
41
|
+
})
|
|
42
|
+
], AppModule);
|
|
43
|
+
export default AppModule;
|
|
44
|
+
//# sourceMappingURL=app.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../../src/app.module.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,aAAa,MAAM,qBAAqB,CAAC;AAChD,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,eAAe,MAAM,wBAAwB,CAAC;AACrD,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAChD,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,2BAA2B,MAAM,qCAAqC,CAAC;AAyB/D,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,SAAS;IAvB7B,MAAM,CAAC;QACN,OAAO,EAAE;YACP,UAAU;YACV,eAAe,CAAC,YAAY,CAAC;gBAC3B,OAAO,EAAE,CAAC,UAAU,CAAC;gBACrB,MAAM,EAAE,CAAC,MAAM,CAAC;gBAChB,UAAU,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC;oBAC/B,UAAU,EAAE;wBACV;4BACE,GAAG,EAAE,MAAM,CAAC,cAAc,EAAE;4BAC5B,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE;yBACjC;qBACF;iBACF,CAAC;aACH,CAAC;SACH;QACD,WAAW,EAAE,CAAC,aAAa,CAAC;QAC5B,SAAS,EAAE;YACT,sBAAsB;YACtB,eAAe;YACf,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;SAC9D;KACF,CAAC;GACmB,SAAS,CAAG;eAAZ,SAAS"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { chmod, mkdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const CERT_FILE = 'fullchain.pem';
|
|
8
|
+
const KEY_FILE = 'privkey.pem';
|
|
9
|
+
const DEFAULT_KEY_BITS = '4096';
|
|
10
|
+
const DEFAULT_DAYS = '825';
|
|
11
|
+
function requiredCertificatePaths(certsDirectory) {
|
|
12
|
+
return {
|
|
13
|
+
certFile: path.join(certsDirectory, CERT_FILE),
|
|
14
|
+
keyFile: path.join(certsDirectory, KEY_FILE),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function hasUsableCertificates(paths) {
|
|
18
|
+
return existsSync(paths.certFile) && existsSync(paths.keyFile);
|
|
19
|
+
}
|
|
20
|
+
export async function ensureCertificates(certsDirectory) {
|
|
21
|
+
await mkdir(certsDirectory, { recursive: true, mode: 0o700 });
|
|
22
|
+
const paths = requiredCertificatePaths(certsDirectory);
|
|
23
|
+
if (!hasUsableCertificates(paths)) {
|
|
24
|
+
await execFileAsync('openssl', [
|
|
25
|
+
'req',
|
|
26
|
+
'-x509',
|
|
27
|
+
'-newkey',
|
|
28
|
+
`rsa:${DEFAULT_KEY_BITS}`,
|
|
29
|
+
'-sha256',
|
|
30
|
+
'-days',
|
|
31
|
+
DEFAULT_DAYS,
|
|
32
|
+
'-nodes',
|
|
33
|
+
'-keyout',
|
|
34
|
+
paths.keyFile,
|
|
35
|
+
'-out',
|
|
36
|
+
paths.certFile,
|
|
37
|
+
'-subj',
|
|
38
|
+
'/CN=localhost',
|
|
39
|
+
]);
|
|
40
|
+
await Promise.all([
|
|
41
|
+
chmod(paths.keyFile, 0o600),
|
|
42
|
+
chmod(paths.certFile, 0o644),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
return paths;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=certificates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"certificates.js","sourceRoot":"","sources":["../../../src/config/certificates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAO1C,MAAM,SAAS,GAAG,eAAe,CAAC;AAClC,MAAM,QAAQ,GAAG,aAAa,CAAC;AAC/B,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,SAAS,wBAAwB,CAAC,cAAsB;IACtD,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;QAC9C,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAuB;IACpD,OAAO,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,cAAsB;IAEtB,MAAM,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC;IACvD,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,aAAa,CAAC,SAAS,EAAE;YAC7B,KAAK;YACL,OAAO;YACP,SAAS;YACT,OAAO,gBAAgB,EAAE;YACzB,SAAS;YACT,OAAO;YACP,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,KAAK,CAAC,OAAO;YACb,MAAM;YACN,KAAK,CAAC,QAAQ;YACd,OAAO;YACP,eAAe;SAChB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC;YAC3B,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function resolveConfigPath(argv: ReadonlyArray<string>): string | undefined;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import resolveFile from './util.js';
|
|
3
|
+
const CONFIG_FLAG = '--config';
|
|
4
|
+
const CONFIG_SHORT_FLAG = '-c';
|
|
5
|
+
const DEFAULT_CONFIG_FILE = 'logger.yaml';
|
|
6
|
+
function parseConfigFromArgs(argv) {
|
|
7
|
+
let configPath;
|
|
8
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
9
|
+
const arg = argv[i];
|
|
10
|
+
if (arg === CONFIG_SHORT_FLAG || arg === CONFIG_FLAG) {
|
|
11
|
+
const value = argv[i + 1];
|
|
12
|
+
if (value === undefined || value.startsWith('-')) {
|
|
13
|
+
throw new Error(`${arg} requires a file path`);
|
|
14
|
+
}
|
|
15
|
+
configPath = value;
|
|
16
|
+
i += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (arg.startsWith(`${CONFIG_FLAG}=`)) {
|
|
20
|
+
const value = arg.slice(`${CONFIG_FLAG}=`.length);
|
|
21
|
+
if (value.trim() === '') {
|
|
22
|
+
throw new Error(`${CONFIG_FLAG} requires a file path`);
|
|
23
|
+
}
|
|
24
|
+
configPath = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return configPath;
|
|
28
|
+
}
|
|
29
|
+
export default function resolveConfigPath(argv) {
|
|
30
|
+
const fromCli = parseConfigFromArgs(argv);
|
|
31
|
+
const fromEnvRaw = process.env.LOGGER_CONFIG_PATH;
|
|
32
|
+
const fromEnv = fromEnvRaw !== undefined && fromEnvRaw.trim() !== ''
|
|
33
|
+
? fromEnvRaw.trim()
|
|
34
|
+
: undefined;
|
|
35
|
+
const selected = fromCli ?? fromEnv ?? DEFAULT_CONFIG_FILE;
|
|
36
|
+
const resolvedPath = resolveFile(selected);
|
|
37
|
+
if (existsSync(resolvedPath)) {
|
|
38
|
+
return resolvedPath;
|
|
39
|
+
}
|
|
40
|
+
if (fromCli !== undefined || fromEnv !== undefined) {
|
|
41
|
+
throw new Error(`Logger config file does not exist: ${resolvedPath}`);
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/config/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,WAAW,MAAM,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAE1C,SAAS,mBAAmB,CAAC,IAA2B;IACtD,IAAI,UAA8B,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,iBAAiB,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACrD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,uBAAuB,CAAC,CAAC;YACjD,CAAC;YACD,UAAU,GAAG,KAAK,CAAC;YACnB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC;YAClD,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,GAAG,WAAW,uBAAuB,CAAC,CAAC;YACzD,CAAC;YACD,UAAU,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,IAA2B;IAE3B,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAClD,MAAM,OAAO,GACX,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE;QAClD,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE;QACnB,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,QAAQ,GAAG,OAAO,IAAI,OAAO,IAAI,mBAAmB,CAAC;IAC3D,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,sCAAsC,YAAY,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type CorsAllowOrigin = string | string[];
|
|
2
|
+
export interface IConfig {
|
|
3
|
+
loadConfig(configPath?: string): void;
|
|
4
|
+
getHostname(): string;
|
|
5
|
+
getPort(): number;
|
|
6
|
+
getCorsAllowOrigin(): CorsAllowOrigin;
|
|
7
|
+
getCorsAllowCredentials(): boolean;
|
|
8
|
+
getAuthToken(): string;
|
|
9
|
+
getTls(): boolean;
|
|
10
|
+
getCertsDirectory(): string;
|
|
11
|
+
getLogFilePath(): string;
|
|
12
|
+
getMaxPayloadBytes(): number;
|
|
13
|
+
getLogMaxBytes(): number;
|
|
14
|
+
getLogRetentionFiles(): number;
|
|
15
|
+
getThrottleTtl(): number;
|
|
16
|
+
getThrottleLimit(): number;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.interface.js","sourceRoot":"","sources":["../../../src/config/config.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CorsAllowOrigin, IConfig } from './config.interface.js';
|
|
2
|
+
export default class Config implements IConfig {
|
|
3
|
+
private configValues;
|
|
4
|
+
constructor();
|
|
5
|
+
loadConfig(configPath?: string): void;
|
|
6
|
+
getHostname(): string;
|
|
7
|
+
getPort(): number;
|
|
8
|
+
getCorsAllowOrigin(): CorsAllowOrigin;
|
|
9
|
+
getCorsAllowCredentials(): boolean;
|
|
10
|
+
getAuthToken(): string;
|
|
11
|
+
getTls(): boolean;
|
|
12
|
+
getCertsDirectory(): string;
|
|
13
|
+
getLogFilePath(): string;
|
|
14
|
+
getMaxPayloadBytes(): number;
|
|
15
|
+
getLogMaxBytes(): number;
|
|
16
|
+
getLogRetentionFiles(): number;
|
|
17
|
+
getThrottleTtl(): number;
|
|
18
|
+
getThrottleLimit(): number;
|
|
19
|
+
private loadYamlConfig;
|
|
20
|
+
private applyEnvOverrides;
|
|
21
|
+
}
|