@bgord/bun 0.5.0 → 0.7.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/dist/api-key-shield.d.ts +2 -2
- package/dist/api-key-shield.d.ts.map +1 -1
- package/dist/api-version.d.ts.map +1 -1
- package/dist/api-version.js +2 -2
- package/dist/api-version.js.map +1 -1
- package/dist/auth-shield.d.ts +11 -10
- package/dist/auth-shield.d.ts.map +1 -1
- package/dist/auth-shield.js +6 -13
- package/dist/auth-shield.js.map +1 -1
- package/dist/basic-auth.d.ts +6 -0
- package/dist/basic-auth.d.ts.map +1 -0
- package/dist/basic-auth.js +4 -0
- package/dist/basic-auth.js.map +1 -0
- package/dist/build-info-repository.d.ts +5 -3
- package/dist/build-info-repository.d.ts.map +1 -0
- package/dist/build-info-repository.js +18 -0
- package/dist/build-info-repository.js.map +1 -0
- package/dist/cache-resolver.d.ts +46 -0
- package/dist/cache-resolver.d.ts.map +1 -0
- package/dist/cache-resolver.js +44 -0
- package/dist/cache-resolver.js.map +1 -0
- package/dist/cache-response.d.ts +0 -4
- package/dist/cache-response.d.ts.map +1 -1
- package/dist/cache-response.js +2 -6
- package/dist/cache-response.js.map +1 -1
- package/dist/cache-static-files.js +3 -3
- package/dist/cache-static-files.js.map +1 -1
- package/dist/context.d.ts +7 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/correlation-id.d.ts +4 -0
- package/dist/correlation-id.d.ts.map +1 -0
- package/dist/correlation-id.js +3 -0
- package/dist/correlation-id.js.map +1 -0
- package/dist/download-file.d.ts +5 -4
- package/dist/download-file.d.ts.map +1 -1
- package/dist/env-validator.d.ts +21 -0
- package/dist/env-validator.d.ts.map +1 -0
- package/dist/env-validator.js +28 -0
- package/dist/env-validator.js.map +1 -0
- package/dist/etag-extractor.d.ts +3 -8
- package/dist/etag-extractor.d.ts.map +1 -1
- package/dist/etag-extractor.js +3 -18
- package/dist/etag-extractor.js.map +1 -1
- package/dist/file-uploader.d.ts +2 -2
- package/dist/file-uploader.d.ts.map +1 -1
- package/dist/file-uploader.js +3 -3
- package/dist/file-uploader.js.map +1 -1
- package/dist/graceful-shutdown.d.ts.map +1 -1
- package/dist/graceful-shutdown.js +3 -3
- package/dist/graceful-shutdown.js.map +1 -1
- package/dist/healthcheck.d.ts +12 -11
- package/dist/healthcheck.d.ts.map +1 -1
- package/dist/healthcheck.js +15 -11
- package/dist/healthcheck.js.map +1 -1
- package/dist/http-logger.d.ts +2 -2
- package/dist/http-logger.d.ts.map +1 -1
- package/dist/http-logger.js +4 -3
- package/dist/http-logger.js.map +1 -1
- package/dist/i18n.d.ts +10 -15
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +10 -25
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +28 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -7
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +1 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +72 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logger.d.ts +23 -7
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +87 -0
- package/dist/logger.js.map +1 -0
- package/dist/mailer.d.ts +23 -1
- package/dist/mailer.d.ts.map +1 -0
- package/dist/mailer.js +33 -0
- package/dist/mailer.js.map +1 -0
- package/dist/memory-consumption.d.ts +3 -2
- package/dist/memory-consumption.d.ts.map +1 -0
- package/dist/memory-consumption.js +8 -0
- package/dist/memory-consumption.js.map +1 -0
- package/dist/new-uuid.d.ts +4 -0
- package/dist/new-uuid.d.ts.map +1 -0
- package/dist/new-uuid.js +6 -0
- package/dist/new-uuid.js.map +1 -0
- package/dist/node-env.vo.d.ts +9 -0
- package/dist/node-env.vo.d.ts.map +1 -0
- package/dist/node-env.vo.js +10 -0
- package/dist/node-env.vo.js.map +1 -0
- package/dist/passwords.d.ts +21 -0
- package/dist/passwords.d.ts.map +1 -0
- package/dist/passwords.js +42 -0
- package/dist/passwords.js.map +1 -0
- package/dist/path.d.ts +4 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +3 -0
- package/dist/path.js.map +1 -0
- package/dist/port.d.ts +4 -0
- package/dist/port.d.ts.map +1 -0
- package/dist/port.js +3 -0
- package/dist/port.js.map +1 -0
- package/dist/prerequisites/binary.d.ts +4 -3
- package/dist/prerequisites/binary.d.ts.map +1 -0
- package/dist/prerequisites/binary.js +29 -0
- package/dist/prerequisites/binary.js.map +1 -0
- package/dist/prerequisites/bun.d.ts +7 -4
- package/dist/prerequisites/bun.d.ts.map +1 -0
- package/dist/prerequisites/bun.js +18 -0
- package/dist/prerequisites/bun.js.map +1 -0
- package/dist/prerequisites/index.d.ts +17 -0
- package/dist/prerequisites/index.d.ts.map +1 -0
- package/dist/prerequisites/index.js +17 -0
- package/dist/prerequisites/index.js.map +1 -0
- package/dist/prerequisites/jobs.d.ts +4 -2
- package/dist/prerequisites/jobs.d.ts.map +1 -0
- package/dist/prerequisites/jobs.js +16 -0
- package/dist/prerequisites/jobs.js.map +1 -0
- package/dist/prerequisites/log-file.d.ts +15 -0
- package/dist/prerequisites/log-file.d.ts.map +1 -0
- package/dist/prerequisites/log-file.js +22 -0
- package/dist/prerequisites/log-file.js.map +1 -0
- package/dist/prerequisites/mailer.d.ts +4 -2
- package/dist/prerequisites/mailer.d.ts.map +1 -0
- package/dist/prerequisites/mailer.js +21 -0
- package/dist/prerequisites/mailer.js.map +1 -0
- package/dist/prerequisites/memory.d.ts +6 -4
- package/dist/prerequisites/memory.d.ts.map +1 -0
- package/dist/prerequisites/memory.js +20 -0
- package/dist/prerequisites/memory.js.map +1 -0
- package/dist/prerequisites/node.d.ts +6 -4
- package/dist/prerequisites/node.d.ts.map +1 -0
- package/dist/prerequisites/node.js +23 -0
- package/dist/prerequisites/node.js.map +1 -0
- package/dist/prerequisites/outside-connectivity.d.ts +4 -2
- package/dist/prerequisites/outside-connectivity.d.ts.map +1 -0
- package/dist/prerequisites/outside-connectivity.js +21 -0
- package/dist/prerequisites/outside-connectivity.js.map +1 -0
- package/dist/prerequisites/path.d.ts +4 -2
- package/dist/prerequisites/path.d.ts.map +1 -0
- package/dist/prerequisites/path.js +26 -0
- package/dist/prerequisites/path.js.map +1 -0
- package/dist/prerequisites/port.d.ts +6 -4
- package/dist/prerequisites/port.d.ts.map +1 -0
- package/dist/prerequisites/port.js +26 -0
- package/dist/prerequisites/port.js.map +1 -0
- package/dist/prerequisites/ram.d.ts +6 -4
- package/dist/prerequisites/ram.d.ts.map +1 -0
- package/dist/prerequisites/ram.js +23 -0
- package/dist/prerequisites/ram.js.map +1 -0
- package/dist/prerequisites/self.d.ts +4 -2
- package/dist/prerequisites/self.d.ts.map +1 -0
- package/dist/prerequisites/self.js +15 -0
- package/dist/prerequisites/self.js.map +1 -0
- package/dist/prerequisites/space.d.ts +6 -4
- package/dist/prerequisites/space.d.ts.map +1 -0
- package/dist/prerequisites/space.js +26 -0
- package/dist/prerequisites/space.js.map +1 -0
- package/dist/prerequisites/ssl-certificate-expiry.d.ts +4 -2
- package/dist/prerequisites/ssl-certificate-expiry.d.ts.map +1 -0
- package/dist/prerequisites/ssl-certificate-expiry.js +24 -0
- package/dist/prerequisites/ssl-certificate-expiry.js.map +1 -0
- package/dist/prerequisites/timezone-utc.d.ts +8 -4
- package/dist/prerequisites/timezone-utc.d.ts.map +1 -0
- package/dist/prerequisites/timezone-utc.js +23 -0
- package/dist/prerequisites/timezone-utc.js.map +1 -0
- package/dist/prerequisites/translations.d.ts +4 -2
- package/dist/prerequisites/translations.d.ts.map +1 -0
- package/dist/prerequisites/translations.js +59 -0
- package/dist/prerequisites/translations.js.map +1 -0
- package/dist/prerequisites.d.ts +2 -0
- package/dist/prerequisites.d.ts.map +1 -0
- package/dist/prerequisites.js +83 -0
- package/dist/prerequisites.js.map +1 -0
- package/dist/rate-limit-shield.d.ts +2 -2
- package/dist/rate-limit-shield.d.ts.map +1 -1
- package/dist/rate-limit-shield.js +2 -2
- package/dist/rate-limit-shield.js.map +1 -1
- package/dist/session-id.d.ts +7 -0
- package/dist/session-id.d.ts.map +1 -0
- package/dist/session-id.js +10 -0
- package/dist/session-id.js.map +1 -0
- package/dist/setup.d.ts +3 -2
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +11 -6
- package/dist/setup.js.map +1 -1
- package/dist/time-zone-offset.d.ts +6 -6
- package/dist/time-zone-offset.d.ts.map +1 -1
- package/dist/time-zone-offset.js +4 -4
- package/dist/time-zone-offset.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/uptime.d.ts +4 -4
- package/dist/uptime.d.ts.map +1 -0
- package/dist/uptime.js +10 -0
- package/dist/uptime.js.map +1 -0
- package/dist/username.d.ts +11 -0
- package/dist/username.d.ts.map +1 -0
- package/dist/username.js +13 -0
- package/dist/username.js.map +1 -0
- package/dist/uuid.d.ts +4 -3
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +4 -0
- package/dist/uuid.js.map +1 -0
- package/dist/weak-etag-extractor.d.ts +7 -0
- package/dist/weak-etag-extractor.d.ts.map +1 -0
- package/dist/weak-etag-extractor.js +18 -0
- package/dist/weak-etag-extractor.js.map +1 -0
- package/package.json +20 -13
- package/src/api-key-shield.ts +2 -2
- package/src/api-version.ts +3 -2
- package/src/auth-shield.ts +14 -23
- package/src/basic-auth.ts +7 -0
- package/src/build-info-repository.ts +26 -0
- package/src/cache-resolver.ts +93 -0
- package/src/cache-response.ts +2 -5
- package/src/cache-static-files.ts +3 -3
- package/src/context.ts +9 -4
- package/src/correlation-id.ts +6 -0
- package/src/download-file.ts +2 -2
- package/src/env-validator.ts +43 -0
- package/src/etag-extractor.ts +5 -20
- package/src/file-uploader.ts +4 -4
- package/src/graceful-shutdown.ts +3 -3
- package/src/healthcheck.ts +30 -20
- package/src/http-logger.ts +9 -5
- package/src/i18n.ts +13 -48
- package/src/index.ts +28 -7
- package/src/jobs.ts +91 -0
- package/src/logger.ts +156 -0
- package/src/mailer.ts +64 -0
- package/src/memory-consumption.ts +9 -0
- package/src/new-uuid.ts +5 -0
- package/src/node-env.vo.ts +9 -0
- package/src/passwords.ts +55 -0
- package/src/path.ts +4 -0
- package/src/port.ts +4 -0
- package/src/prerequisites/binary.ts +44 -0
- package/src/prerequisites/bun.ts +32 -0
- package/src/prerequisites/index.ts +16 -0
- package/src/prerequisites/jobs.ts +28 -0
- package/src/prerequisites/log-file.ts +35 -0
- package/src/prerequisites/mailer.ts +32 -0
- package/src/prerequisites/memory.ts +34 -0
- package/src/prerequisites/node.ts +36 -0
- package/src/prerequisites/outside-connectivity.ts +31 -0
- package/src/prerequisites/path.ts +41 -0
- package/src/prerequisites/port.ts +44 -0
- package/src/prerequisites/ram.ts +35 -0
- package/src/prerequisites/self.ts +24 -0
- package/src/prerequisites/space.ts +39 -0
- package/src/prerequisites/ssl-certificate-expiry.ts +37 -0
- package/src/prerequisites/timezone-utc.ts +36 -0
- package/src/prerequisites/translations.ts +92 -0
- package/src/prerequisites.ts +105 -0
- package/src/rate-limit-shield.ts +3 -3
- package/src/session-id.ts +13 -0
- package/src/setup.ts +14 -7
- package/src/time-zone-offset.ts +11 -14
- package/src/uptime.ts +16 -0
- package/src/username.ts +20 -0
- package/src/uuid.ts +6 -0
- package/src/weak-etag-extractor.ts +19 -0
- package/src/image-processor.ts +0 -5
package/src/healthcheck.ts
CHANGED
|
@@ -1,28 +1,38 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as tools from "@bgord/tools";
|
|
2
2
|
import { createFactory } from "hono/factory";
|
|
3
3
|
|
|
4
|
+
import { BuildInfoRepository } from "./build-info-repository";
|
|
5
|
+
import { MemoryConsumption } from "./memory-consumption";
|
|
6
|
+
import {
|
|
7
|
+
AbstractPrerequisite,
|
|
8
|
+
BasePrerequisiteConfig,
|
|
9
|
+
PrerequisiteLabelType,
|
|
10
|
+
PrerequisiteStatusEnum,
|
|
11
|
+
} from "./prerequisites";
|
|
12
|
+
import { Uptime, UptimeResultType } from "./uptime";
|
|
13
|
+
|
|
4
14
|
const handler = createFactory();
|
|
5
15
|
|
|
6
16
|
type HealthcheckResultType = {
|
|
7
|
-
ok:
|
|
8
|
-
version:
|
|
17
|
+
ok: PrerequisiteStatusEnum;
|
|
18
|
+
version: tools.BuildVersionType;
|
|
9
19
|
details: {
|
|
10
|
-
label:
|
|
11
|
-
status:
|
|
20
|
+
label: PrerequisiteLabelType;
|
|
21
|
+
status: PrerequisiteStatusEnum;
|
|
12
22
|
}[];
|
|
13
|
-
uptime:
|
|
23
|
+
uptime: UptimeResultType;
|
|
14
24
|
memory: {
|
|
15
|
-
bytes:
|
|
16
|
-
formatted: ReturnType<
|
|
25
|
+
bytes: tools.Size["bytes"];
|
|
26
|
+
formatted: ReturnType<tools.Size["format"]>;
|
|
17
27
|
};
|
|
18
|
-
} &
|
|
28
|
+
} & tools.StopwatchResultType;
|
|
19
29
|
|
|
20
30
|
export class Healthcheck {
|
|
21
|
-
static build = (prerequisites:
|
|
31
|
+
static build = (prerequisites: AbstractPrerequisite<BasePrerequisiteConfig>[]) =>
|
|
22
32
|
handler.createHandlers(async (c) => {
|
|
23
|
-
const stopwatch = new
|
|
33
|
+
const stopwatch = new tools.Stopwatch();
|
|
24
34
|
|
|
25
|
-
const build = await
|
|
35
|
+
const build = await BuildInfoRepository.extract();
|
|
26
36
|
|
|
27
37
|
const details: HealthcheckResultType["details"][number][] = [];
|
|
28
38
|
|
|
@@ -31,20 +41,20 @@ export class Healthcheck {
|
|
|
31
41
|
details.push({ label: prerequisite.label, status });
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
const ok = details.every((result) => result.status !==
|
|
35
|
-
?
|
|
36
|
-
:
|
|
44
|
+
const ok = details.every((result) => result.status !== PrerequisiteStatusEnum.failure)
|
|
45
|
+
? PrerequisiteStatusEnum.success
|
|
46
|
+
: PrerequisiteStatusEnum.failure;
|
|
37
47
|
|
|
38
|
-
const code = ok ===
|
|
48
|
+
const code = ok === PrerequisiteStatusEnum.success ? 200 : 424;
|
|
39
49
|
|
|
40
50
|
const result: HealthcheckResultType = {
|
|
41
51
|
ok,
|
|
42
52
|
details,
|
|
43
|
-
version: build.BUILD_VERSION ??
|
|
44
|
-
uptime:
|
|
53
|
+
version: build.BUILD_VERSION ?? tools.BuildVersion.parse("unknown"),
|
|
54
|
+
uptime: Uptime.get(),
|
|
45
55
|
memory: {
|
|
46
|
-
bytes:
|
|
47
|
-
formatted:
|
|
56
|
+
bytes: MemoryConsumption.get().toBytes(),
|
|
57
|
+
formatted: MemoryConsumption.get().format(tools.SizeUnit.MB),
|
|
48
58
|
},
|
|
49
59
|
...stopwatch.stop(),
|
|
50
60
|
};
|
package/src/http-logger.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import * as bg from "@bgord/node";
|
|
2
1
|
import { getConnInfo } from "hono/bun";
|
|
3
2
|
import { createMiddleware } from "hono/factory";
|
|
4
3
|
import _ from "lodash";
|
|
5
4
|
|
|
5
|
+
import { CacheHitEnum } from "./cache-resolver";
|
|
6
|
+
import { CacheResponse } from "./cache-response";
|
|
7
|
+
import { CorrelationIdType } from "./correlation-id";
|
|
8
|
+
import { Logger } from "./logger";
|
|
9
|
+
|
|
6
10
|
export class HttpLogger {
|
|
7
11
|
private static simplify(response: unknown) {
|
|
8
12
|
const result = JSON.stringify(response, (_key, value) =>
|
|
@@ -34,9 +38,9 @@ export class HttpLogger {
|
|
|
34
38
|
"if-none-match",
|
|
35
39
|
];
|
|
36
40
|
|
|
37
|
-
static build = (logger:
|
|
41
|
+
static build = (logger: Logger) =>
|
|
38
42
|
createMiddleware(async (c, next) => {
|
|
39
|
-
const correlationId = c.get("requestId") as
|
|
43
|
+
const correlationId = c.get("requestId") as CorrelationIdType;
|
|
40
44
|
const info = getConnInfo(c);
|
|
41
45
|
const url = c.req.url;
|
|
42
46
|
const method = c.req.method;
|
|
@@ -71,9 +75,9 @@ export class HttpLogger {
|
|
|
71
75
|
|
|
72
76
|
await next();
|
|
73
77
|
|
|
74
|
-
const cacheHitHeader = c.res.clone().headers.get(
|
|
78
|
+
const cacheHitHeader = c.res.clone().headers.get(CacheResponse.CACHE_HIT_HEADER);
|
|
75
79
|
|
|
76
|
-
const cacheHit = cacheHitHeader ===
|
|
80
|
+
const cacheHit = cacheHitHeader === CacheHitEnum.hit ? CacheHitEnum.hit : undefined;
|
|
77
81
|
|
|
78
82
|
let response: any;
|
|
79
83
|
try {
|
package/src/i18n.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import * as
|
|
3
|
-
|
|
4
|
-
import {
|
|
2
|
+
import * as tools from "@bgord/tools";
|
|
3
|
+
|
|
4
|
+
import { Path, PathType } from "./path";
|
|
5
5
|
|
|
6
6
|
export type TranslationsKeyType = string;
|
|
7
7
|
export type TranslationsValueType = string;
|
|
@@ -12,51 +12,19 @@ export type TranslationPlaceholderValueType = string | number;
|
|
|
12
12
|
export type TranslationVariableType = Record<TranslationPlaceholderType, TranslationPlaceholderValueType>;
|
|
13
13
|
|
|
14
14
|
export type I18nConfigType = {
|
|
15
|
-
translationsPath?:
|
|
16
|
-
defaultLanguage?:
|
|
17
|
-
supportedLanguages: Record<string,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type I18nVariablesType = {
|
|
21
|
-
language: bgn.Schema.LanguageType;
|
|
22
|
-
supportedLanguages: bgn.Schema.LanguageType[];
|
|
23
|
-
translationsPath: bgn.Schema.PathType;
|
|
15
|
+
translationsPath?: PathType;
|
|
16
|
+
defaultLanguage?: tools.LanguageType;
|
|
17
|
+
supportedLanguages: Record<string, tools.LanguageType>;
|
|
24
18
|
};
|
|
25
19
|
|
|
26
20
|
export class I18n {
|
|
27
|
-
static
|
|
28
|
-
|
|
29
|
-
// TODO: fix bgord/node types
|
|
30
|
-
static DEFAULT_TRANSLATIONS_PATH = bgn.Schema.Path.parse("infra/translations") as string;
|
|
31
|
-
|
|
32
|
-
static FALLBACK_LANGUAGE = "en";
|
|
33
|
-
|
|
34
|
-
static applyTo(config: I18nConfigType) {
|
|
35
|
-
return createMiddleware(async (c, next) => {
|
|
36
|
-
const translationsPath = config?.translationsPath ?? I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
21
|
+
static DEFAULT_TRANSLATIONS_PATH = Path.parse("infra/translations");
|
|
37
22
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const chosenLanguage = getCookie(c, I18n.LANGUAGE_COOKIE_NAME) ?? defaultLanguage;
|
|
41
|
-
|
|
42
|
-
const language = Object.keys(config.supportedLanguages).find((language) => language === chosenLanguage)
|
|
43
|
-
? chosenLanguage
|
|
44
|
-
: I18n.FALLBACK_LANGUAGE;
|
|
45
|
-
|
|
46
|
-
c.set("supportedLanguages", Object.keys(config.supportedLanguages));
|
|
47
|
-
c.set("language", language);
|
|
48
|
-
c.set("translationsPath", translationsPath);
|
|
49
|
-
|
|
50
|
-
return next();
|
|
51
|
-
});
|
|
52
|
-
}
|
|
23
|
+
constructor(private translationsPath: PathType = I18n.DEFAULT_TRANSLATIONS_PATH) {}
|
|
53
24
|
|
|
54
|
-
|
|
55
|
-
language: bgn.Schema.LanguageType,
|
|
56
|
-
translationsPath: bgn.Schema.PathType,
|
|
57
|
-
): Promise<TranslationsType> {
|
|
25
|
+
async getTranslations(language: tools.LanguageType): Promise<TranslationsType> {
|
|
58
26
|
try {
|
|
59
|
-
return Bun.file(
|
|
27
|
+
return Bun.file(this.getTranslationPathForLanguage(language)).json();
|
|
60
28
|
} catch (error) {
|
|
61
29
|
// biome-ignore lint: lint/suspicious/noConsoleLog
|
|
62
30
|
console.log("I18n#getTranslations", error);
|
|
@@ -65,7 +33,7 @@ export class I18n {
|
|
|
65
33
|
}
|
|
66
34
|
}
|
|
67
35
|
|
|
68
|
-
|
|
36
|
+
useTranslations(translations: TranslationsType) {
|
|
69
37
|
return function translate(key: TranslationsKeyType, variables?: TranslationVariableType) {
|
|
70
38
|
const translation = translations[key];
|
|
71
39
|
|
|
@@ -83,10 +51,7 @@ export class I18n {
|
|
|
83
51
|
};
|
|
84
52
|
}
|
|
85
53
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
translationsPath = I18n.DEFAULT_TRANSLATIONS_PATH,
|
|
89
|
-
): bgn.Schema.PathType {
|
|
90
|
-
return bgn.Schema.Path.parse(path.join(translationsPath, `${language}.json`));
|
|
54
|
+
getTranslationPathForLanguage(language: tools.LanguageType): PathType {
|
|
55
|
+
return Path.parse(path.join(this.translationsPath, `${language}.json`));
|
|
91
56
|
}
|
|
92
57
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
export * from "./api-key-shield";
|
|
2
2
|
export * from "./api-version";
|
|
3
3
|
export * from "./auth-shield";
|
|
4
|
+
export * from "./basic-auth";
|
|
5
|
+
export * from "./build-info-repository";
|
|
6
|
+
export * from "./cache-resolver";
|
|
7
|
+
export * from "./cache-response";
|
|
8
|
+
export * from "./cache-static-files";
|
|
4
9
|
export * from "./context";
|
|
10
|
+
export * from "./correlation-id";
|
|
11
|
+
export * from "./download-file";
|
|
12
|
+
export * from "./env-validator";
|
|
5
13
|
export * from "./etag-extractor";
|
|
14
|
+
export * from "./file-uploader";
|
|
6
15
|
export * from "./graceful-shutdown";
|
|
7
16
|
export * from "./healthcheck";
|
|
8
17
|
export * from "./http-logger";
|
|
9
|
-
export * from "./rate-limit-shield";
|
|
10
|
-
export * from "./time-zone-offset";
|
|
11
|
-
export * from "./cache-static-files";
|
|
12
|
-
export * from "./cache-response";
|
|
13
|
-
export * from "./download-file";
|
|
14
18
|
export * from "./i18n";
|
|
15
|
-
export * from "./
|
|
16
|
-
export * from "./
|
|
19
|
+
export * from "./jobs";
|
|
20
|
+
export * from "./logger";
|
|
21
|
+
export * from "./mailer";
|
|
22
|
+
export * from "./memory-consumption";
|
|
23
|
+
export * from "./new-uuid";
|
|
24
|
+
export * from "./node-env.vo";
|
|
25
|
+
export * from "./passwords";
|
|
26
|
+
export * from "./path";
|
|
27
|
+
export * from "./port";
|
|
28
|
+
export * from "./prerequisites";
|
|
29
|
+
export * from "./prerequisites/index";
|
|
30
|
+
export * from "./rate-limit-shield";
|
|
31
|
+
export * from "./session-id";
|
|
17
32
|
export * from "./setup";
|
|
33
|
+
export * from "./time-zone-offset";
|
|
34
|
+
export * from "./uptime";
|
|
35
|
+
export * from "./username";
|
|
36
|
+
export * from "./uuid";
|
|
37
|
+
export * from "./weak-etag-extractor";
|
|
38
|
+
export { default as sharp } from "sharp";
|
package/src/jobs.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as tools from "@bgord/tools";
|
|
2
|
+
import { Cron } from "croner";
|
|
3
|
+
|
|
4
|
+
import { CorrelationId } from "./correlation-id";
|
|
5
|
+
import { Logger } from "./logger";
|
|
6
|
+
import { NewUUID } from "./new-uuid";
|
|
7
|
+
|
|
8
|
+
export type JobNameType = string;
|
|
9
|
+
export type MultipleJobsType = Record<JobNameType, Cron>;
|
|
10
|
+
|
|
11
|
+
export enum UTC_DAY_OF_THE_WEEK {
|
|
12
|
+
Monday = 1,
|
|
13
|
+
Tuesday = 2,
|
|
14
|
+
Wednesday = 3,
|
|
15
|
+
Thursday = 4,
|
|
16
|
+
Friday = 5,
|
|
17
|
+
Saturday = 6,
|
|
18
|
+
Sunday = 0,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Jobs {
|
|
22
|
+
static SCHEDULES = { EVERY_MINUTE: "* * * * *", EVERY_HOUR: "0 * * * *" };
|
|
23
|
+
|
|
24
|
+
static stopAll(jobs: MultipleJobsType) {
|
|
25
|
+
Object.values(jobs).forEach((job) => job.stop());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static areAllRunning(jobs: MultipleJobsType): boolean {
|
|
29
|
+
return Object.values(jobs).every((job) => job.isRunning());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type JobProcessorType = {
|
|
34
|
+
cron: string;
|
|
35
|
+
label: JobNameType;
|
|
36
|
+
process: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class JobHandler {
|
|
40
|
+
constructor(private readonly logger: Logger) {}
|
|
41
|
+
|
|
42
|
+
handle(jobProcessor: JobProcessorType) {
|
|
43
|
+
const correlationId = CorrelationId.parse(NewUUID.generate());
|
|
44
|
+
|
|
45
|
+
// biome-ignore lint: lint/complexity/noUselessThisAlias
|
|
46
|
+
const that = this;
|
|
47
|
+
|
|
48
|
+
return async () => {
|
|
49
|
+
const stopwatch = new tools.Stopwatch();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
that.logger.info({
|
|
53
|
+
message: `${jobProcessor.label} start`,
|
|
54
|
+
operation: "job_start",
|
|
55
|
+
correlationId,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await jobProcessor.process();
|
|
59
|
+
|
|
60
|
+
that.logger.info({
|
|
61
|
+
message: `${jobProcessor.label} success`,
|
|
62
|
+
operation: "job_success",
|
|
63
|
+
correlationId,
|
|
64
|
+
metadata: stopwatch.stop(),
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
that.logger.error({
|
|
68
|
+
message: `${jobProcessor.label} error`,
|
|
69
|
+
operation: "job_error",
|
|
70
|
+
correlationId,
|
|
71
|
+
metadata: {
|
|
72
|
+
...that.logger.formatError(error),
|
|
73
|
+
...stopwatch.stop(),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protect(cron: Cron) {
|
|
81
|
+
// biome-ignore lint: lint/complexity/noUselessThisAlias
|
|
82
|
+
const that = this;
|
|
83
|
+
|
|
84
|
+
return async () => {
|
|
85
|
+
that.logger.info({
|
|
86
|
+
message: `${cron.name} overrun`,
|
|
87
|
+
operation: "job_overrun",
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as tools from "@bgord/tools";
|
|
2
|
+
import * as winston from "winston";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
|
|
5
|
+
import { CorrelationIdType } from "./correlation-id";
|
|
6
|
+
import { NodeEnvironmentEnum } from "./node-env.vo";
|
|
7
|
+
import { PathType } from "./path";
|
|
8
|
+
|
|
9
|
+
export enum LogLevelEnum {
|
|
10
|
+
/** @public */
|
|
11
|
+
silent = "silent",
|
|
12
|
+
error = "error",
|
|
13
|
+
warn = "warn",
|
|
14
|
+
info = "info",
|
|
15
|
+
http = "http",
|
|
16
|
+
verbose = "verbose",
|
|
17
|
+
}
|
|
18
|
+
export const LogLevel = z.enum(LogLevelEnum);
|
|
19
|
+
|
|
20
|
+
type LogTimestampType = number;
|
|
21
|
+
type LogAppType = string;
|
|
22
|
+
type LogEnvironmentType = NodeEnvironmentEnum;
|
|
23
|
+
type LogMessageType = string;
|
|
24
|
+
type LogOperationType = string;
|
|
25
|
+
type LogMetadataType = Record<string, any>;
|
|
26
|
+
type LogCorrelationIdType = CorrelationIdType;
|
|
27
|
+
|
|
28
|
+
const levels: Record<LogLevelEnum, number> = {
|
|
29
|
+
silent: 0,
|
|
30
|
+
error: 0,
|
|
31
|
+
warn: 1,
|
|
32
|
+
info: 2,
|
|
33
|
+
http: 3,
|
|
34
|
+
verbose: 4,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type LogFullType = {
|
|
38
|
+
timestamp: LogTimestampType;
|
|
39
|
+
app: LogAppType;
|
|
40
|
+
environment: LogEnvironmentType;
|
|
41
|
+
level: LogLevelEnum;
|
|
42
|
+
message: LogMessageType;
|
|
43
|
+
operation: LogOperationType;
|
|
44
|
+
method: string;
|
|
45
|
+
url: string;
|
|
46
|
+
client: { ip?: string; userAgent?: string };
|
|
47
|
+
correlationId?: LogCorrelationIdType;
|
|
48
|
+
responseCode?: number;
|
|
49
|
+
durationMs?: tools.TimestampType;
|
|
50
|
+
metadata?: LogMetadataType;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type LogErrorType = Omit<
|
|
54
|
+
LogFullType,
|
|
55
|
+
"app" | "client" | "environment" | "duration" | "level" | "method" | "responseCode" | "timestamp" | "url"
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
type LogWarnType = Omit<
|
|
59
|
+
LogFullType,
|
|
60
|
+
"app" | "client" | "environment" | "duration" | "level" | "method" | "responseCode" | "timestamp" | "url"
|
|
61
|
+
>;
|
|
62
|
+
|
|
63
|
+
type LogInfoType = Omit<
|
|
64
|
+
LogFullType,
|
|
65
|
+
"app" | "client" | "environment" | "duration" | "level" | "method" | "responseCode" | "timestamp" | "url"
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
type LogHttpType = Omit<LogFullType, "app" | "environment" | "timestamp" | "level">;
|
|
69
|
+
|
|
70
|
+
type LoggerOptionsType = {
|
|
71
|
+
app: LogAppType;
|
|
72
|
+
environment: NodeEnvironmentEnum;
|
|
73
|
+
level?: LogLevelEnum;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export class Logger {
|
|
77
|
+
private readonly instance: winston.Logger;
|
|
78
|
+
|
|
79
|
+
private readonly app: LoggerOptionsType["app"];
|
|
80
|
+
|
|
81
|
+
private readonly environment: LoggerOptionsType["environment"];
|
|
82
|
+
|
|
83
|
+
private readonly level: LoggerOptionsType["level"] = LogLevelEnum.verbose;
|
|
84
|
+
|
|
85
|
+
constructor(options: LoggerOptionsType) {
|
|
86
|
+
this.app = options.app;
|
|
87
|
+
this.environment = options.environment;
|
|
88
|
+
this.level = options.level ?? LogLevelEnum.verbose;
|
|
89
|
+
|
|
90
|
+
const formats = [
|
|
91
|
+
winston.format.json(),
|
|
92
|
+
|
|
93
|
+
this.environment !== NodeEnvironmentEnum.production ? winston.format.prettyPrint() : undefined,
|
|
94
|
+
].filter(Boolean);
|
|
95
|
+
|
|
96
|
+
this.instance = winston.createLogger({
|
|
97
|
+
level: this.level,
|
|
98
|
+
levels,
|
|
99
|
+
handleExceptions: true,
|
|
100
|
+
handleRejections: true,
|
|
101
|
+
format: winston.format.combine(...(formats as NonNullable<winston.LoggerOptions["format"]>[])),
|
|
102
|
+
transports: [new winston.transports.Console()],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (this.environment === NodeEnvironmentEnum.production) {
|
|
106
|
+
this.instance.add(
|
|
107
|
+
new winston.transports.File({
|
|
108
|
+
filename: this.getProductionLogFilePath(),
|
|
109
|
+
maxsize: tools.Size.toBytes({ unit: tools.SizeUnit.MB, value: 100 }),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private getBase() {
|
|
116
|
+
return {
|
|
117
|
+
app: this.app,
|
|
118
|
+
environment: this.environment,
|
|
119
|
+
timestamp: Date.now(),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
info(log: LogInfoType) {
|
|
124
|
+
this.instance.info({ level: LogLevelEnum.info, ...this.getBase(), ...log });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
error(log: LogErrorType) {
|
|
128
|
+
this.instance.error({
|
|
129
|
+
level: LogLevelEnum.error,
|
|
130
|
+
...this.getBase(),
|
|
131
|
+
...log,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
warn(log: LogWarnType) {
|
|
136
|
+
this.instance.warn({ level: LogLevelEnum.warn, ...this.getBase(), ...log });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
http(log: LogHttpType) {
|
|
140
|
+
this.instance.http({ level: LogLevelEnum.http, ...this.getBase(), ...log });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getProductionLogFilePath(): PathType {
|
|
144
|
+
return `/var/log/${this.app}-${this.environment}.log`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
formatError(_error: unknown) {
|
|
148
|
+
const error = _error as Error;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
message: error?.message,
|
|
152
|
+
name: error?.name,
|
|
153
|
+
stack: error?.stack,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/mailer.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import nodemailer, { SendMailOptions } from "nodemailer";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
|
|
4
|
+
import { Path } from "./path";
|
|
5
|
+
import { Port } from "./port";
|
|
6
|
+
|
|
7
|
+
export const SmtpHost = z.string().trim().min(1);
|
|
8
|
+
export type SmtpHostType = z.infer<typeof SmtpHost>;
|
|
9
|
+
|
|
10
|
+
export const SmtpPort = Port;
|
|
11
|
+
export type SmtpPortType = z.infer<typeof SmtpPort>;
|
|
12
|
+
|
|
13
|
+
export const SmtpUser = z.string().trim().min(1);
|
|
14
|
+
export type SmtpUserType = z.infer<typeof SmtpUser>;
|
|
15
|
+
|
|
16
|
+
export const SmtpPass = z.string().trim().min(1);
|
|
17
|
+
export type SmtpPassType = z.infer<typeof SmtpPass>;
|
|
18
|
+
|
|
19
|
+
type MailerConfigType = {
|
|
20
|
+
SMTP_HOST: SmtpHostType;
|
|
21
|
+
SMTP_PORT: SmtpPortType;
|
|
22
|
+
SMTP_USER: SmtpUserType;
|
|
23
|
+
SMTP_PASS: SmtpPassType;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type MailerSendOptionsType = SendMailOptions;
|
|
27
|
+
|
|
28
|
+
export const EmailSubject = z.string().min(1).max(128);
|
|
29
|
+
export type EmailSubjectType = z.infer<typeof EmailSubject>;
|
|
30
|
+
|
|
31
|
+
export const EmailContentHtml = z.string().min(1).max(10_000);
|
|
32
|
+
export type EmailContentHtmlType = z.infer<typeof EmailContentHtml>;
|
|
33
|
+
|
|
34
|
+
export const EmailFrom = z.email();
|
|
35
|
+
export type EmailFromType = z.infer<typeof EmailFrom>;
|
|
36
|
+
|
|
37
|
+
export const EmailTo = z.email();
|
|
38
|
+
export type EmailToType = z.infer<typeof EmailTo>;
|
|
39
|
+
|
|
40
|
+
export const EmailAttachment = z.object({ filename: Path, path: Path });
|
|
41
|
+
export type EmailAttachmentType = z.infer<typeof EmailAttachment>;
|
|
42
|
+
|
|
43
|
+
export class Mailer {
|
|
44
|
+
private readonly transport: nodemailer.Transporter;
|
|
45
|
+
|
|
46
|
+
constructor(config: MailerConfigType) {
|
|
47
|
+
this.transport = nodemailer.createTransport({
|
|
48
|
+
host: config.SMTP_HOST,
|
|
49
|
+
port: config.SMTP_PORT,
|
|
50
|
+
auth: {
|
|
51
|
+
user: config.SMTP_USER,
|
|
52
|
+
pass: config.SMTP_PASS,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async send(options: MailerSendOptionsType): Promise<unknown> {
|
|
58
|
+
return this.transport.sendMail(options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async verify() {
|
|
62
|
+
return this.transport.verify();
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/new-uuid.ts
ADDED
package/src/passwords.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export type PasswordType = string;
|
|
4
|
+
type HashedPasswordType = string;
|
|
5
|
+
|
|
6
|
+
export class Password {
|
|
7
|
+
private schema: z.ZodSchema = z.string().min(1).max(256);
|
|
8
|
+
|
|
9
|
+
private value: PasswordType;
|
|
10
|
+
|
|
11
|
+
constructor(value: PasswordType, schema?: z.ZodSchema) {
|
|
12
|
+
this.schema = schema ?? this.schema;
|
|
13
|
+
this.value = this.schema.parse(value) as PasswordType;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async hash(): Promise<HashedPassword> {
|
|
17
|
+
return HashedPassword.fromPassword(this);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
read(): PasswordType {
|
|
21
|
+
return this.value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class HashedPassword {
|
|
26
|
+
private constructor(private value: HashedPasswordType) {}
|
|
27
|
+
|
|
28
|
+
static async fromPassword(password: Password) {
|
|
29
|
+
const hash = await Bun.password.hash(password.read());
|
|
30
|
+
|
|
31
|
+
return new HashedPassword(hash);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static async fromHash(value: HashedPasswordType) {
|
|
35
|
+
return new HashedPassword(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
read(): HashedPasswordType {
|
|
39
|
+
return this.value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async matches(password: Password): Promise<boolean> {
|
|
43
|
+
return Bun.password.verify(password.read(), this.read());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async matchesOrThrow(password: Password): Promise<true> {
|
|
47
|
+
const matches = await Bun.password.verify(password.read(), this.read());
|
|
48
|
+
|
|
49
|
+
if (!matches) {
|
|
50
|
+
throw new Error("HashedPassword does not match the provided password");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/path.ts
ADDED
package/src/port.ts
ADDED