@bgord/bun 1.2.5 → 1.2.7
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/dist/better-auth-logger.service.d.ts +5 -2
- package/dist/better-auth-logger.service.d.ts.map +1 -1
- package/dist/better-auth-logger.service.js +6 -6
- package/dist/better-auth-logger.service.js.map +1 -1
- package/dist/command-logger.service.d.ts +6 -2
- package/dist/command-logger.service.d.ts.map +1 -1
- package/dist/command-logger.service.js +4 -4
- package/dist/command-logger.service.js.map +1 -1
- package/dist/content-hash-noop.adapter.d.ts +7 -0
- package/dist/content-hash-noop.adapter.d.ts.map +1 -0
- package/dist/content-hash-noop.adapter.js +6 -0
- package/dist/content-hash-noop.adapter.js.map +1 -0
- package/dist/content-hash-sha256-bun.adapter.d.ts +7 -0
- package/dist/content-hash-sha256-bun.adapter.d.ts.map +1 -0
- package/dist/content-hash-sha256-bun.adapter.js +8 -0
- package/dist/content-hash-sha256-bun.adapter.js.map +1 -0
- package/dist/content-hash.port.d.ts +7 -0
- package/dist/content-hash.port.d.ts.map +1 -0
- package/dist/content-hash.port.js +2 -0
- package/dist/content-hash.port.js.map +1 -0
- package/dist/encryption-bun.adapter.d.ts +6 -2
- package/dist/encryption-bun.adapter.d.ts.map +1 -1
- package/dist/encryption-bun.adapter.js +5 -5
- package/dist/encryption-bun.adapter.js.map +1 -1
- package/dist/event-handler.service.d.ts +6 -2
- package/dist/event-handler.service.d.ts.map +1 -1
- package/dist/event-handler.service.js +4 -4
- package/dist/event-handler.service.js.map +1 -1
- package/dist/event-logger.service.d.ts +6 -2
- package/dist/event-logger.service.d.ts.map +1 -1
- package/dist/event-logger.service.js +4 -4
- package/dist/event-logger.service.js.map +1 -1
- package/dist/graceful-shutdown.service.d.ts +5 -2
- package/dist/graceful-shutdown.service.d.ts.map +1 -1
- package/dist/graceful-shutdown.service.js +18 -10
- package/dist/graceful-shutdown.service.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs.service.d.ts.map +1 -1
- package/dist/jobs.service.js.map +1 -1
- package/dist/mailer-smtp-with-logger.adapter.d.ts +5 -5
- package/dist/mailer-smtp-with-logger.adapter.d.ts.map +1 -1
- package/dist/mailer-smtp-with-logger.adapter.js +8 -8
- package/dist/mailer-smtp-with-logger.adapter.js.map +1 -1
- package/dist/markdown-generator-marked.adapter.d.ts +7 -0
- package/dist/markdown-generator-marked.adapter.d.ts.map +1 -0
- package/dist/markdown-generator-marked.adapter.js +12 -0
- package/dist/markdown-generator-marked.adapter.js.map +1 -0
- package/dist/markdown-generator-noop.adapter.d.ts +5 -0
- package/dist/markdown-generator-noop.adapter.d.ts.map +1 -0
- package/dist/markdown-generator-noop.adapter.js +6 -0
- package/dist/markdown-generator-noop.adapter.js.map +1 -0
- package/dist/markdown-generator.port.d.ts +4 -0
- package/dist/markdown-generator.port.d.ts.map +1 -0
- package/dist/markdown-generator.port.js +3 -0
- package/dist/markdown-generator.port.js.map +1 -0
- package/dist/pdf-generator-noop.adapter.d.ts +6 -2
- package/dist/pdf-generator-noop.adapter.d.ts.map +1 -1
- package/dist/pdf-generator-noop.adapter.js +8 -4
- package/dist/pdf-generator-noop.adapter.js.map +1 -1
- package/dist/prerequisites/clock-drift.d.ts +2 -2
- package/dist/prerequisites/clock-drift.js +3 -3
- package/dist/prerequisites/dns.d.ts +16 -0
- package/dist/prerequisites/dns.d.ts.map +1 -0
- package/dist/prerequisites/dns.js +30 -0
- package/dist/prerequisites/dns.js.map +1 -0
- package/dist/prerequisites/index.d.ts +1 -0
- package/dist/prerequisites/index.d.ts.map +1 -1
- package/dist/prerequisites/index.js +1 -0
- package/dist/prerequisites/index.js.map +1 -1
- package/dist/prerequisites/log-file.d.ts +2 -2
- package/dist/prerequisites/log-file.js +3 -3
- package/dist/prerequisites/mailer.d.ts +2 -2
- package/dist/prerequisites/mailer.js +3 -3
- package/dist/prerequisites/space.d.ts +2 -2
- package/dist/prerequisites/space.d.ts.map +1 -1
- package/dist/prerequisites/space.js +3 -3
- package/dist/prerequisites/space.js.map +1 -1
- package/dist/prerequisites/ssl-certificate-expiry.d.ts +2 -2
- package/dist/prerequisites/ssl-certificate-expiry.d.ts.map +1 -1
- package/dist/prerequisites/ssl-certificate-expiry.js +3 -3
- package/dist/prerequisites/ssl-certificate-expiry.js.map +1 -1
- package/dist/prerequisites/translations.d.ts +4 -4
- package/dist/prerequisites/translations.js +5 -5
- package/dist/prerequisites.service.d.ts +2 -2
- package/dist/prerequisites.service.js +3 -3
- package/dist/remote-file-storage-noop.adapter.d.ts +6 -3
- package/dist/remote-file-storage-noop.adapter.d.ts.map +1 -1
- package/dist/remote-file-storage-noop.adapter.js +10 -8
- package/dist/remote-file-storage-noop.adapter.js.map +1 -1
- package/dist/shield-rate-limit.middleware.d.ts.map +1 -1
- package/dist/shield-rate-limit.middleware.js.map +1 -1
- package/dist/timekeeper-noop.adapter.d.ts +6 -2
- package/dist/timekeeper-noop.adapter.d.ts.map +1 -1
- package/dist/timekeeper-noop.adapter.js +4 -4
- package/dist/timekeeper-noop.adapter.js.map +1 -1
- package/dist/translations.service.d.ts.map +1 -1
- package/dist/translations.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -6
- package/readme.md +7 -0
- package/src/better-auth-logger.service.ts +6 -4
- package/src/command-logger.service.ts +4 -2
- package/src/content-hash-noop.adapter.ts +7 -0
- package/src/content-hash-sha256-bun.adapter.ts +10 -0
- package/src/content-hash.port.ts +5 -0
- package/src/encryption-bun.adapter.ts +5 -3
- package/src/event-handler.service.ts +4 -2
- package/src/event-logger.service.ts +4 -2
- package/src/graceful-shutdown.service.ts +18 -8
- package/src/index.ts +6 -0
- package/src/jobs.service.ts +1 -0
- package/src/mailer-smtp-with-logger.adapter.ts +8 -7
- package/src/markdown-generator-marked.adapter.ts +15 -0
- package/src/markdown-generator-noop.adapter.ts +7 -0
- package/src/markdown-generator.port.ts +3 -0
- package/src/pdf-generator-noop.adapter.ts +8 -2
- package/src/prerequisites/clock-drift.ts +4 -4
- package/src/prerequisites/dns.ts +36 -0
- package/src/prerequisites/index.ts +1 -0
- package/src/prerequisites/log-file.ts +4 -4
- package/src/prerequisites/mailer.ts +4 -4
- package/src/prerequisites/space.ts +4 -4
- package/src/prerequisites/ssl-certificate-expiry.ts +4 -4
- package/src/prerequisites/translations.ts +7 -7
- package/src/prerequisites.service.ts +4 -4
- package/src/remote-file-storage-noop.adapter.ts +13 -9
- package/src/shield-rate-limit.middleware.ts +1 -0
- package/src/timekeeper-noop.adapter.ts +4 -2
- package/src/translations.service.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/bun",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Bartosz Gordon",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"@types/lodash": "4.17.21",
|
|
28
28
|
"@types/nodemailer": "7.0.4",
|
|
29
29
|
"@types/yazl": "3.3.0",
|
|
30
|
-
"cspell": "9.
|
|
30
|
+
"cspell": "9.4.0",
|
|
31
31
|
"knip": "5.71.0",
|
|
32
|
-
"lefthook": "2.0.
|
|
32
|
+
"lefthook": "2.0.7",
|
|
33
33
|
"only-allow": "1.2.2",
|
|
34
34
|
"shellcheck": "4.1.0",
|
|
35
35
|
"typescript": "5.9.3",
|
|
@@ -37,14 +37,16 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@axiomhq/winston": "1.3.1",
|
|
40
|
-
"@bgord/tools": "1.1.
|
|
41
|
-
"@hono/ua-blocker": "0.1.
|
|
42
|
-
"better-auth": "1.4.
|
|
40
|
+
"@bgord/tools": "1.1.11",
|
|
41
|
+
"@hono/ua-blocker": "0.1.20",
|
|
42
|
+
"better-auth": "1.4.5",
|
|
43
43
|
"croner": "9.1.0",
|
|
44
44
|
"csv": "6.4.1",
|
|
45
45
|
"hcaptcha": "0.2.0",
|
|
46
46
|
"hono": "4.10.7",
|
|
47
|
+
"isomorphic-dompurify": "2.33.0",
|
|
47
48
|
"lodash": "4.17.21",
|
|
49
|
+
"marked": "17.0.1",
|
|
48
50
|
"node-cache": "5.1.2",
|
|
49
51
|
"nodemailer": "7.0.11",
|
|
50
52
|
"sharp": "0.34.5",
|
package/readme.md
CHANGED
|
@@ -50,6 +50,9 @@ src/
|
|
|
50
50
|
├── command-envelope.ts
|
|
51
51
|
├── command-logger.service.ts
|
|
52
52
|
├── command.types.ts
|
|
53
|
+
├── content-hash-noop.adapter.ts
|
|
54
|
+
├── content-hash-sha256-bun.adapter.ts
|
|
55
|
+
├── content-hash.port.ts
|
|
53
56
|
├── context.middleware.ts
|
|
54
57
|
├── correlation-id.vo.ts
|
|
55
58
|
├── correlation-storage.service.ts
|
|
@@ -148,6 +151,9 @@ src/
|
|
|
148
151
|
├── mailer-smtp.adapter.ts
|
|
149
152
|
├── mailer.port.ts
|
|
150
153
|
├── mailer.vo.ts
|
|
154
|
+
├── markdown-generator-marked.adapter.ts
|
|
155
|
+
├── markdown-generator-noop.adapter.ts
|
|
156
|
+
├── markdown-generator.port.ts
|
|
151
157
|
├── memory-consumption.service.ts
|
|
152
158
|
├── modules
|
|
153
159
|
│ ├── history
|
|
@@ -194,6 +200,7 @@ src/
|
|
|
194
200
|
│ ├── clock-drift.ts
|
|
195
201
|
│ ├── dependency-vulnerabilities.ts
|
|
196
202
|
│ ├── directory.ts
|
|
203
|
+
│ ├── dns.ts
|
|
197
204
|
│ ├── external-api.ts
|
|
198
205
|
│ ├── jobs.ts
|
|
199
206
|
│ ├── log-file.ts
|
|
@@ -3,8 +3,10 @@ import { formatError } from "./logger-format-error.service";
|
|
|
3
3
|
|
|
4
4
|
type LogLevel = "info" | "success" | "warn" | "error" | "debug";
|
|
5
5
|
|
|
6
|
+
type Dependencies = { Logger: LoggerPort };
|
|
7
|
+
|
|
6
8
|
export class BetterAuthLogger {
|
|
7
|
-
constructor(private readonly
|
|
9
|
+
constructor(private readonly deps: Dependencies) {}
|
|
8
10
|
|
|
9
11
|
attach() {
|
|
10
12
|
return {
|
|
@@ -16,18 +18,18 @@ export class BetterAuthLogger {
|
|
|
16
18
|
|
|
17
19
|
switch (level) {
|
|
18
20
|
case LogLevelEnum.error: {
|
|
19
|
-
this.
|
|
21
|
+
this.deps.Logger.error({
|
|
20
22
|
...base,
|
|
21
23
|
error: formatError(args.find((a) => a instanceof Error) ?? new Error(message)),
|
|
22
24
|
});
|
|
23
25
|
break;
|
|
24
26
|
}
|
|
25
27
|
case LogLevelEnum.warn: {
|
|
26
|
-
this.
|
|
28
|
+
this.deps.Logger.warn(base);
|
|
27
29
|
break;
|
|
28
30
|
}
|
|
29
31
|
default: {
|
|
30
|
-
this.
|
|
32
|
+
this.deps.Logger.info(base);
|
|
31
33
|
break;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { LoggerPort } from "./logger.port";
|
|
2
2
|
|
|
3
|
+
type Dependencies = { Logger: LoggerPort };
|
|
4
|
+
|
|
3
5
|
export class CommandLogger {
|
|
4
6
|
private readonly base = { component: "infra", operation: "command_emitted" };
|
|
5
7
|
|
|
6
|
-
constructor(private readonly
|
|
8
|
+
constructor(private readonly deps: Dependencies) {}
|
|
7
9
|
|
|
8
10
|
private _handle(
|
|
9
11
|
type: string,
|
|
@@ -14,7 +16,7 @@ export class CommandLogger {
|
|
|
14
16
|
if (type === "subscribe") return;
|
|
15
17
|
if (typeof commandName === "symbol") return;
|
|
16
18
|
|
|
17
|
-
this.
|
|
19
|
+
this.deps.Logger.info({ message: `${commandName} emitted`, metadata: commandData, ...this.base });
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
handle = this._handle.bind(this);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContentHashPort } from "./content-hash.port";
|
|
2
|
+
|
|
3
|
+
export class ContentHashSha256BunAdapter implements ContentHashPort {
|
|
4
|
+
async hash(content: string) {
|
|
5
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content));
|
|
6
|
+
const etag = Buffer.from(digest).toString("hex");
|
|
7
|
+
|
|
8
|
+
return { etag };
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -4,11 +4,13 @@ import { EncryptionIV } from "./encryption-iv.vo";
|
|
|
4
4
|
|
|
5
5
|
export const EncryptionBunAdapterError = { InvalidPayload: "encryption.bun.adapter.invalid.payload" };
|
|
6
6
|
|
|
7
|
+
type Dependencies = { CryptoKeyProvider: CryptoKeyProviderPort };
|
|
8
|
+
|
|
7
9
|
export class EncryptionBunAdapter implements EncryptionPort {
|
|
8
|
-
constructor(private readonly
|
|
10
|
+
constructor(private readonly deps: Dependencies) {}
|
|
9
11
|
|
|
10
12
|
async encrypt(recipe: EncryptionRecipe) {
|
|
11
|
-
const key = await this.
|
|
13
|
+
const key = await this.deps.CryptoKeyProvider.get();
|
|
12
14
|
const iv = EncryptionIV.generate();
|
|
13
15
|
|
|
14
16
|
const plaintext = await Bun.file(recipe.input.get()).arrayBuffer();
|
|
@@ -30,7 +32,7 @@ export class EncryptionBunAdapter implements EncryptionPort {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
async decrypt(recipe: EncryptionRecipe) {
|
|
33
|
-
const key = await this.
|
|
35
|
+
const key = await this.deps.CryptoKeyProvider.get();
|
|
34
36
|
|
|
35
37
|
const bytes = new Uint8Array(await Bun.file(recipe.input.get()).arrayBuffer());
|
|
36
38
|
if (bytes.length < EncryptionIV.LENGTH + 1) throw new Error(EncryptionBunAdapterError.InvalidPayload);
|
|
@@ -3,15 +3,17 @@ import type { GenericEventSchema } from "./event.types";
|
|
|
3
3
|
import type { LoggerPort } from "./logger.port";
|
|
4
4
|
import { formatError } from "./logger-format-error.service";
|
|
5
5
|
|
|
6
|
+
type Dependencies = { Logger: LoggerPort };
|
|
7
|
+
|
|
6
8
|
export class EventHandler {
|
|
7
|
-
constructor(private readonly
|
|
9
|
+
constructor(private readonly deps: Dependencies) {}
|
|
8
10
|
|
|
9
11
|
handle<T extends { name: z.infer<GenericEventSchema["shape"]["name"]> }>(fn: (event: T) => Promise<void>) {
|
|
10
12
|
return async (event: T) => {
|
|
11
13
|
try {
|
|
12
14
|
await fn(event);
|
|
13
15
|
} catch (error) {
|
|
14
|
-
this.
|
|
16
|
+
this.deps.Logger.error({
|
|
15
17
|
message: `Unknown ${event.name} event handler error`,
|
|
16
18
|
component: "infra",
|
|
17
19
|
operation: "unknown_event_handler_error",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { LoggerPort } from "./logger.port";
|
|
2
2
|
|
|
3
|
+
type Dependencies = { Logger: LoggerPort };
|
|
4
|
+
|
|
3
5
|
export class EventLogger {
|
|
4
6
|
private readonly base = { component: "infra", operation: "event_emitted" };
|
|
5
7
|
|
|
6
|
-
constructor(private readonly
|
|
8
|
+
constructor(private readonly deps: Dependencies) {}
|
|
7
9
|
|
|
8
10
|
private _handle(
|
|
9
11
|
type: string,
|
|
@@ -14,7 +16,7 @@ export class EventLogger {
|
|
|
14
16
|
if (type === "subscribe") return;
|
|
15
17
|
if (typeof eventName === "symbol") return;
|
|
16
18
|
|
|
17
|
-
this.
|
|
19
|
+
this.deps.Logger.info({ message: `${eventName} emitted`, metadata: eventData, ...this.base });
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
handle = this._handle.bind(this);
|
|
@@ -4,12 +4,14 @@ import { formatError } from "./logger-format-error.service";
|
|
|
4
4
|
|
|
5
5
|
type ServerType = ReturnType<typeof Bun.serve>;
|
|
6
6
|
|
|
7
|
+
type Dependencies = { Logger: LoggerPort };
|
|
8
|
+
|
|
7
9
|
export class GracefulShutdown {
|
|
8
10
|
private readonly base = { operation: "shutdown", component: "infra" } as const;
|
|
9
11
|
private isShuttingDown = false;
|
|
10
12
|
|
|
11
13
|
constructor(
|
|
12
|
-
private readonly
|
|
14
|
+
private readonly deps: Dependencies,
|
|
13
15
|
private readonly exitFn: (code: number) => never = ((code: number) => process.exit(code)) as never,
|
|
14
16
|
) {}
|
|
15
17
|
|
|
@@ -20,36 +22,44 @@ export class GracefulShutdown {
|
|
|
20
22
|
try {
|
|
21
23
|
server.stop();
|
|
22
24
|
} catch (error) {
|
|
23
|
-
this.
|
|
25
|
+
this.deps.Logger.error({ message: "Server stop failed", error: formatError(error), ...this.base });
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
Promise.resolve()
|
|
27
29
|
.then(() => cleanup())
|
|
28
|
-
.then(() => this.
|
|
30
|
+
.then(() => this.deps.Logger.info({ message: "HTTP server closed", ...this.base }))
|
|
29
31
|
.catch((error) =>
|
|
30
|
-
this.
|
|
32
|
+
this.deps.Logger.error({ message: "Cleanup hook failed", error: formatError(error), ...this.base }),
|
|
31
33
|
)
|
|
32
34
|
.finally(() => this.exitFn(exitCode));
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
applyTo(server: ServerType, cleanup: () => any = tools.noop) {
|
|
36
38
|
process.once("SIGTERM", () => {
|
|
37
|
-
this.
|
|
39
|
+
this.deps.Logger.info({ message: "SIGTERM received", ...this.base });
|
|
38
40
|
this.shutdown(server, cleanup, 0);
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
process.once("SIGINT", () => {
|
|
42
|
-
this.
|
|
44
|
+
this.deps.Logger.info({ message: "SIGINT received", ...this.base });
|
|
43
45
|
this.shutdown(server, cleanup, 0);
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
process.once("unhandledRejection", (reason) => {
|
|
47
|
-
this.
|
|
49
|
+
this.deps.Logger.error({
|
|
50
|
+
message: "UnhandledRejection received",
|
|
51
|
+
error: formatError(reason),
|
|
52
|
+
...this.base,
|
|
53
|
+
});
|
|
48
54
|
this.shutdown(server, cleanup, 1);
|
|
49
55
|
});
|
|
50
56
|
|
|
51
57
|
process.once("uncaughtException", (error) => {
|
|
52
|
-
this.
|
|
58
|
+
this.deps.Logger.error({
|
|
59
|
+
message: "UncaughtException received",
|
|
60
|
+
error: formatError(error),
|
|
61
|
+
...this.base,
|
|
62
|
+
});
|
|
53
63
|
this.shutdown(server, cleanup, 1);
|
|
54
64
|
});
|
|
55
65
|
}
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,9 @@ export * from "./clock-system.adapter";
|
|
|
23
23
|
export * from "./command.types";
|
|
24
24
|
export * from "./command-envelope";
|
|
25
25
|
export * from "./command-logger.service";
|
|
26
|
+
export * from "./content-hash.port";
|
|
27
|
+
export * from "./content-hash-noop.adapter";
|
|
28
|
+
export * from "./content-hash-sha256-bun.adapter";
|
|
26
29
|
export * from "./context.middleware";
|
|
27
30
|
export * from "./correlation-id.vo";
|
|
28
31
|
export * from "./correlation-storage.service";
|
|
@@ -124,6 +127,9 @@ export * from "./mailer.vo";
|
|
|
124
127
|
export * from "./mailer-noop.adapter";
|
|
125
128
|
export * from "./mailer-smtp.adapter";
|
|
126
129
|
export * from "./mailer-smtp-with-logger.adapter";
|
|
130
|
+
export * from "./markdown-generator.port";
|
|
131
|
+
export * from "./markdown-generator-marked.adapter";
|
|
132
|
+
export * from "./markdown-generator-noop.adapter";
|
|
127
133
|
export * from "./memory-consumption.service";
|
|
128
134
|
export * as History from "./modules/history";
|
|
129
135
|
export * as Preferences from "./modules/preferences";
|
package/src/jobs.service.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { formatError } from "./logger-format-error.service";
|
|
|
9
9
|
export type JobNameType = string;
|
|
10
10
|
export type MultipleJobsType = Record<JobNameType, Cron>;
|
|
11
11
|
export type JobProcessorType = { cron: string; label: JobNameType; process: () => Promise<void> };
|
|
12
|
+
|
|
12
13
|
type Dependencies = { Logger: LoggerPort; IdProvider: IdProviderPort; Clock: ClockPort };
|
|
13
14
|
|
|
14
15
|
export class Jobs {
|
|
@@ -5,28 +5,29 @@ import type { MailerPort } from "./mailer.port";
|
|
|
5
5
|
import type { MailerSmtpAdapter } from "./mailer-smtp.adapter";
|
|
6
6
|
|
|
7
7
|
type MailerSendOptionsType = SendMailOptions;
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
type Dependencies = { MailerSmtp: MailerSmtpAdapter; Logger: LoggerPort };
|
|
9
10
|
|
|
10
11
|
export class MailerSmtpWithLoggerAdapter implements MailerPort {
|
|
11
12
|
private readonly base = { component: "infra", operation: "mailer" };
|
|
12
13
|
|
|
13
|
-
constructor(private readonly
|
|
14
|
+
constructor(private readonly deps: Dependencies) {}
|
|
14
15
|
|
|
15
16
|
async send(message: MailerSendOptionsType): Promise<unknown> {
|
|
16
17
|
try {
|
|
17
|
-
this.
|
|
18
|
-
const result = await this.
|
|
19
|
-
this.
|
|
18
|
+
this.deps.Logger.info({ message: "Mailer attempt", metadata: message, ...this.base });
|
|
19
|
+
const result = await this.deps.MailerSmtp.send(message);
|
|
20
|
+
this.deps.Logger.info({ message: "Mailer success", metadata: { message, result }, ...this.base });
|
|
20
21
|
|
|
21
22
|
return result;
|
|
22
23
|
} catch (error) {
|
|
23
|
-
this.
|
|
24
|
+
this.deps.Logger.error({ message: "Mailer error", error: formatError(error), ...this.base });
|
|
24
25
|
|
|
25
26
|
throw error;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
async verify() {
|
|
30
|
-
return this.
|
|
31
|
+
return this.deps.MailerSmtp.verify();
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import DOMPurify from "isomorphic-dompurify";
|
|
2
|
+
import { Marked } from "marked";
|
|
3
|
+
import type { MarkdownGeneratorPort } from "./markdown-generator.port";
|
|
4
|
+
|
|
5
|
+
export class MarkdownGeneratorMarkedAdapter implements MarkdownGeneratorPort {
|
|
6
|
+
private readonly instance: Marked;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.instance = new Marked();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async generate(content: string) {
|
|
13
|
+
return DOMPurify.sanitize(await this.instance.parse(content));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -4,13 +4,19 @@ import type { PdfGeneratorPort, PdfGeneratorTemplateType } from "./pdf-generator
|
|
|
4
4
|
export const PLACEHOLDER_PDF_BASE64 =
|
|
5
5
|
"JVBERi0xLjQKMSAwIG9iago8PC9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoK MiAwIG9iago8PC9UeXBlIC9QYWdlcwovS2lkcyBbMyAwIFJdCi9Db3VudCAxCj4+CmVuZG9iagoz IDAgb2JqCjw8L1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCA1OTUgODQy XQovQ29udGVudHMgNSAwIFIKL1Jlc291cmNlcyA8PC9Qcm9jU2V0IFsvUERGIC9UZXh0XQovRm9u dCA8PC9GMSA0IDAgUj4+Cj4+Cj4+CmVuZG9iago0IDAgb2JqCjw8L1R5cGUgL0ZvbnQKL1N1YnR5 cGUgL1R5cGUxCi9OYW1lIC9GMQovQmFzZUZvbnQgL0hlbHZldGljYQovRW5jb2RpbmcgL01hY1Jv bWFuRW5jb2RpbmcKPj4KZW5kb2JqCjUgMCBvYmoKPDwvTGVuZ3RoIDUzCj4+CnN0cmVhbQpCVAov RjEgMjAgVGYKMjIwIDQwMCBUZAooRHVtbXkgUERGKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhy ZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZgowMDAwMDAwMDA5IDAwMDAwIG4KMDAwMDAwMDA2MyAw MDAwMCBuCjAwMDAwMDAxMjQgMDAwMDAgbgowMDAwMDAwMjc3IDAwMDAwIG4KMDAwMDAwMDM5MiAw MDAwMCBuCnRyYWlsZXIKPDwvU2l6ZSA2Ci9Sb290IDEgMCBSCj4+CnN0YXJ0eHJlZgo0OTUKJSVF T0YK";
|
|
6
6
|
|
|
7
|
+
type Dependencies = { Logger: LoggerPort };
|
|
8
|
+
|
|
7
9
|
export class PdfGeneratorNoopAdapter implements PdfGeneratorPort {
|
|
8
10
|
private readonly base = { component: "infra", operation: "pdf_generator" };
|
|
9
11
|
|
|
10
|
-
constructor(private readonly
|
|
12
|
+
constructor(private readonly deps: Dependencies) {}
|
|
11
13
|
|
|
12
14
|
async request(template: PdfGeneratorTemplateType, data: Record<string, unknown>) {
|
|
13
|
-
this.
|
|
15
|
+
this.deps.Logger.info({
|
|
16
|
+
message: "[NOOP] PDF generator adapter",
|
|
17
|
+
metadata: { template, data },
|
|
18
|
+
...this.base,
|
|
19
|
+
});
|
|
14
20
|
|
|
15
21
|
return Buffer.from(PLACEHOLDER_PDF_BASE64, "base64");
|
|
16
22
|
}
|
|
@@ -11,13 +11,13 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
11
11
|
readonly enabled?: boolean = true;
|
|
12
12
|
|
|
13
13
|
readonly skew: tools.Duration;
|
|
14
|
-
readonly
|
|
14
|
+
readonly Timekeeper: TimekeeperPort;
|
|
15
15
|
readonly timeout: tools.Duration;
|
|
16
16
|
|
|
17
17
|
constructor(
|
|
18
18
|
config: prereqs.PrerequisiteConfigType & {
|
|
19
19
|
skew: tools.Duration;
|
|
20
|
-
|
|
20
|
+
Timekeeper?: TimekeeperPort;
|
|
21
21
|
timeout?: tools.Duration;
|
|
22
22
|
},
|
|
23
23
|
) {
|
|
@@ -25,7 +25,7 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
25
25
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
26
26
|
|
|
27
27
|
this.skew = config.skew;
|
|
28
|
-
this.
|
|
28
|
+
this.Timekeeper = config.Timekeeper ?? new TimekeeperGoogleAdapter();
|
|
29
29
|
this.timeout = config.timeout ?? tools.Duration.Seconds(2);
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -36,7 +36,7 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
38
|
const timestamp = await Timeout.cancellable(
|
|
39
|
-
(signal: AbortSignal) => this.
|
|
39
|
+
(signal: AbortSignal) => this.Timekeeper.get(signal),
|
|
40
40
|
this.timeout,
|
|
41
41
|
);
|
|
42
42
|
if (!timestamp) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import dns from "dns/promises";
|
|
2
|
+
import * as tools from "@bgord/tools";
|
|
3
|
+
import type { ClockPort } from "../clock.port";
|
|
4
|
+
import * as prereqs from "../prerequisites.service";
|
|
5
|
+
import { Timeout } from "../timeout.service";
|
|
6
|
+
|
|
7
|
+
export class PrerequisiteDNS implements prereqs.Prerequisite {
|
|
8
|
+
readonly kind = "dns";
|
|
9
|
+
readonly label: prereqs.PrerequisiteLabelType;
|
|
10
|
+
readonly enabled?: boolean = true;
|
|
11
|
+
|
|
12
|
+
private readonly hostname: string;
|
|
13
|
+
readonly timeout: tools.Duration;
|
|
14
|
+
|
|
15
|
+
constructor(config: prereqs.PrerequisiteConfigType & { hostname: string; timeout?: tools.Duration }) {
|
|
16
|
+
this.label = config.label;
|
|
17
|
+
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
18
|
+
|
|
19
|
+
this.hostname = config.hostname;
|
|
20
|
+
this.timeout = config.timeout ?? tools.Duration.Seconds(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
24
|
+
const stopwatch = new tools.Stopwatch(clock.now());
|
|
25
|
+
|
|
26
|
+
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await Timeout.run(dns.lookup(this.hostname), this.timeout);
|
|
30
|
+
|
|
31
|
+
return prereqs.Verification.success(stopwatch.stop());
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return prereqs.Verification.failure(stopwatch.stop(), error as Error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -8,13 +8,13 @@ export class PrerequisiteLogFile implements prereqs.Prerequisite {
|
|
|
8
8
|
readonly label: prereqs.PrerequisiteLabelType;
|
|
9
9
|
readonly enabled?: boolean = true;
|
|
10
10
|
|
|
11
|
-
private readonly
|
|
11
|
+
private readonly Logger: LoggerWinstonProductionAdapter;
|
|
12
12
|
|
|
13
|
-
constructor(config: prereqs.PrerequisiteConfigType & {
|
|
13
|
+
constructor(config: prereqs.PrerequisiteConfigType & { Logger: LoggerWinstonProductionAdapter }) {
|
|
14
14
|
this.label = config.label;
|
|
15
15
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
16
16
|
|
|
17
|
-
this.
|
|
17
|
+
this.Logger = config.Logger;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -23,7 +23,7 @@ export class PrerequisiteLogFile implements prereqs.Prerequisite {
|
|
|
23
23
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
|
-
const path = this.
|
|
26
|
+
const path = this.Logger.prodLogFile;
|
|
27
27
|
const result = await Bun.file(path).exists();
|
|
28
28
|
|
|
29
29
|
if (result) return prereqs.Verification.success(stopwatch.stop());
|
|
@@ -9,14 +9,14 @@ export class PrerequisiteMailer implements prereqs.Prerequisite {
|
|
|
9
9
|
readonly label: prereqs.PrerequisiteLabelType;
|
|
10
10
|
readonly enabled?: boolean = true;
|
|
11
11
|
|
|
12
|
-
private readonly
|
|
12
|
+
private readonly Mailer: MailerPort;
|
|
13
13
|
readonly timeout: tools.Duration;
|
|
14
14
|
|
|
15
|
-
constructor(config: prereqs.PrerequisiteConfigType & {
|
|
15
|
+
constructor(config: prereqs.PrerequisiteConfigType & { Mailer: MailerPort; timeout?: tools.Duration }) {
|
|
16
16
|
this.label = config.label;
|
|
17
17
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
18
18
|
|
|
19
|
-
this.
|
|
19
|
+
this.Mailer = config.Mailer;
|
|
20
20
|
this.timeout = config.timeout ?? tools.Duration.Seconds(2);
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ export class PrerequisiteMailer implements prereqs.Prerequisite {
|
|
|
26
26
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
|
-
await Timeout.run(this.
|
|
29
|
+
await Timeout.run(this.Mailer.verify(), this.timeout);
|
|
30
30
|
return prereqs.Verification.success(stopwatch.stop());
|
|
31
31
|
} catch (error) {
|
|
32
32
|
return prereqs.Verification.failure(stopwatch.stop(), error as Error);
|
|
@@ -11,16 +11,16 @@ export class PrerequisiteSpace implements prereqs.Prerequisite {
|
|
|
11
11
|
readonly enabled?: boolean = true;
|
|
12
12
|
|
|
13
13
|
private readonly minimum: tools.Size;
|
|
14
|
-
private readonly
|
|
14
|
+
private readonly DiskSpaceChecker: DiskSpaceCheckerPort;
|
|
15
15
|
|
|
16
16
|
constructor(
|
|
17
|
-
config: prereqs.PrerequisiteConfigType & { minimum: tools.Size;
|
|
17
|
+
config: prereqs.PrerequisiteConfigType & { minimum: tools.Size; DiskSpaceChecker?: DiskSpaceCheckerPort },
|
|
18
18
|
) {
|
|
19
19
|
this.label = config.label;
|
|
20
20
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
21
21
|
|
|
22
22
|
this.minimum = config.minimum;
|
|
23
|
-
this.
|
|
23
|
+
this.DiskSpaceChecker = config.DiskSpaceChecker ?? new DiskSpaceCheckerBunAdapter();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -30,7 +30,7 @@ export class PrerequisiteSpace implements prereqs.Prerequisite {
|
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
const root = path.sep;
|
|
33
|
-
const freeDiskSpace = await this.
|
|
33
|
+
const freeDiskSpace = await this.DiskSpaceChecker.get(root);
|
|
34
34
|
|
|
35
35
|
if (freeDiskSpace.isGreaterThan(this.minimum)) return prereqs.Verification.success(stopwatch.stop());
|
|
36
36
|
return prereqs.Verification.failure(stopwatch.stop(), {
|
|
@@ -10,13 +10,13 @@ export class PrerequisiteSSLCertificateExpiry implements prereqs.Prerequisite {
|
|
|
10
10
|
|
|
11
11
|
private readonly host: string;
|
|
12
12
|
private readonly days: number;
|
|
13
|
-
private readonly
|
|
13
|
+
private readonly CertificateInspector: CertificateInspectorPort;
|
|
14
14
|
|
|
15
15
|
constructor(
|
|
16
16
|
config: prereqs.PrerequisiteConfigType & {
|
|
17
17
|
host: string;
|
|
18
18
|
days: number;
|
|
19
|
-
|
|
19
|
+
CertificateInspector: CertificateInspectorPort;
|
|
20
20
|
},
|
|
21
21
|
) {
|
|
22
22
|
this.label = config.label;
|
|
@@ -24,7 +24,7 @@ export class PrerequisiteSSLCertificateExpiry implements prereqs.Prerequisite {
|
|
|
24
24
|
|
|
25
25
|
this.host = config.host;
|
|
26
26
|
this.days = config.days;
|
|
27
|
-
this.
|
|
27
|
+
this.CertificateInspector = config.CertificateInspector;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -32,7 +32,7 @@ export class PrerequisiteSSLCertificateExpiry implements prereqs.Prerequisite {
|
|
|
32
32
|
|
|
33
33
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
34
34
|
|
|
35
|
-
const result = await this.
|
|
35
|
+
const result = await this.CertificateInspector.inspect(this.host);
|
|
36
36
|
|
|
37
37
|
if (!result.success)
|
|
38
38
|
return prereqs.Verification.failure(stopwatch.stop(), { message: "Certificate unavailable" });
|
|
@@ -23,15 +23,15 @@ export class PrerequisiteTranslations implements prereqs.Prerequisite {
|
|
|
23
23
|
private readonly translationsPath?: typeof I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
24
24
|
private readonly supportedLanguages: types.I18nConfigType["supportedLanguages"];
|
|
25
25
|
|
|
26
|
-
private readonly
|
|
27
|
-
private readonly
|
|
26
|
+
private readonly Logger: LoggerPort;
|
|
27
|
+
private readonly JsonFileReader: JsonFileReaderPort;
|
|
28
28
|
|
|
29
29
|
constructor(
|
|
30
30
|
config: prereqs.PrerequisiteConfigType & {
|
|
31
31
|
translationsPath?: typeof I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
32
32
|
supportedLanguages: types.I18nConfigType["supportedLanguages"];
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
Logger: LoggerPort;
|
|
34
|
+
JsonFileReader?: JsonFileReaderPort;
|
|
35
35
|
},
|
|
36
36
|
) {
|
|
37
37
|
this.label = config.label;
|
|
@@ -40,8 +40,8 @@ export class PrerequisiteTranslations implements prereqs.Prerequisite {
|
|
|
40
40
|
this.translationsPath = config.translationsPath;
|
|
41
41
|
this.supportedLanguages = config.supportedLanguages;
|
|
42
42
|
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
43
|
+
this.Logger = config.Logger;
|
|
44
|
+
this.JsonFileReader = config.JsonFileReader ?? new JsonFileReaderBunForgivingAdapter();
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -52,7 +52,7 @@ export class PrerequisiteTranslations implements prereqs.Prerequisite {
|
|
|
52
52
|
const translationsPath = this.translationsPath ?? I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
53
53
|
|
|
54
54
|
const supportedLanguages = Object.keys(this.supportedLanguages);
|
|
55
|
-
const i18n = new I18n({ Logger: this.
|
|
55
|
+
const i18n = new I18n({ Logger: this.Logger, JsonFileReader: this.JsonFileReader });
|
|
56
56
|
|
|
57
57
|
try {
|
|
58
58
|
await fsp.access(translationsPath, constants.R_OK);
|