@devopsplaybook.io/common-utils 1.0.0-beta.5.9c5ecde
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/.github/workflows/main-build.yml +17 -0
- package/.github/workflows/npm-upgrade.yml +16 -0
- package/.github/workflows/pr-check.yml +26 -0
- package/.github/workflows/reusable-merge-build.yml +141 -0
- package/.github/workflows/reusable-npm-merge.yml +135 -0
- package/.github/workflows/reusable-npm-pr.yml +153 -0
- package/.github/workflows/reusable-npm-upgrade.yml +92 -0
- package/.github/workflows/reusable-pr-verify.yml +135 -0
- package/README.md +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +24 -0
- package/dist/src/ConfigBase.d.ts +104 -0
- package/dist/src/ConfigBase.js +201 -0
- package/dist/src/DbUtils.d.ts +50 -0
- package/dist/src/DbUtils.js +117 -0
- package/dist/src/DbUtilsNoTelemetry.d.ts +27 -0
- package/dist/src/DbUtilsNoTelemetry.js +89 -0
- package/dist/src/OTelContext.d.ts +35 -0
- package/dist/src/OTelContext.js +42 -0
- package/dist/src/PostgresDbUtils.d.ts +52 -0
- package/dist/src/PostgresDbUtils.js +217 -0
- package/dist/src/SqlDbUtils.d.ts +40 -0
- package/dist/src/SqlDbUtils.js +156 -0
- package/dist/src/SystemCommand.d.ts +9 -0
- package/dist/src/SystemCommand.js +56 -0
- package/dist/src/Timeout.d.ts +6 -0
- package/dist/src/Timeout.js +15 -0
- package/eslint.config.mjs +10 -0
- package/index.ts +8 -0
- package/jest.config.js +13 -0
- package/package.json +50 -0
- package/prettierrc.json +5 -0
- package/src/ConfigBase.spec.ts +108 -0
- package/src/ConfigBase.ts +213 -0
- package/src/DbUtils.spec.ts +23 -0
- package/src/DbUtils.ts +118 -0
- package/src/DbUtilsNoTelemetry.spec.ts +174 -0
- package/src/DbUtilsNoTelemetry.ts +121 -0
- package/src/OTelContext.spec.ts +58 -0
- package/src/OTelContext.ts +65 -0
- package/src/PostgresDbUtils.spec.ts +155 -0
- package/src/PostgresDbUtils.ts +233 -0
- package/src/SqlDbUtils.spec.ts +111 -0
- package/src/SqlDbUtils.ts +153 -0
- package/src/SystemCommand.spec.ts +18 -0
- package/src/SystemCommand.ts +23 -0
- package/src/Timeout.spec.ts +18 -0
- package/src/Timeout.ts +12 -0
- package/tsconfig.json +14 -0
- package/tsconfig.spec.json +7 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
name: "Reusable: PR Verify"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
docker_platforms:
|
|
7
|
+
description: "Docker platforms to build for"
|
|
8
|
+
required: false
|
|
9
|
+
type: string
|
|
10
|
+
default: "linux/arm64/v8,linux/amd64"
|
|
11
|
+
node_app_directories:
|
|
12
|
+
description: "JSON array of Node.js app directories to build, lint, and test"
|
|
13
|
+
required: false
|
|
14
|
+
type: string
|
|
15
|
+
default: ""
|
|
16
|
+
node_version:
|
|
17
|
+
description: "Node.js version to use"
|
|
18
|
+
required: false
|
|
19
|
+
type: string
|
|
20
|
+
default: "22"
|
|
21
|
+
secrets:
|
|
22
|
+
DOCKER_HUB_USERNAME:
|
|
23
|
+
required: true
|
|
24
|
+
DOCKER_HUB_ACCESS_TOKEN:
|
|
25
|
+
required: true
|
|
26
|
+
QUALITY_DASHBOARD_TOKEN:
|
|
27
|
+
required: false
|
|
28
|
+
QUALITY_DASHBOARD_URL:
|
|
29
|
+
required: false
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
node-build:
|
|
33
|
+
if: inputs.node_app_directories != ''
|
|
34
|
+
strategy:
|
|
35
|
+
matrix:
|
|
36
|
+
app: ${{ fromJSON(inputs.node_app_directories) }}
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
defaults:
|
|
39
|
+
run:
|
|
40
|
+
working-directory: ${{ matrix.app }}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v6
|
|
43
|
+
|
|
44
|
+
- name: Setup Node.js
|
|
45
|
+
uses: actions/setup-node@v4
|
|
46
|
+
with:
|
|
47
|
+
node-version: ${{ inputs.node_version }}
|
|
48
|
+
cache: "npm"
|
|
49
|
+
cache-dependency-path: ${{ matrix.app }}/package-lock.json
|
|
50
|
+
|
|
51
|
+
- name: Install dependencies
|
|
52
|
+
run: npm ci
|
|
53
|
+
|
|
54
|
+
- name: Build
|
|
55
|
+
run: npm run build
|
|
56
|
+
|
|
57
|
+
- name: Lint
|
|
58
|
+
run: npm run lint
|
|
59
|
+
|
|
60
|
+
- name: Test
|
|
61
|
+
run: npm run test
|
|
62
|
+
|
|
63
|
+
- name: Zip coverage and upload to Quality Dashboard
|
|
64
|
+
if: always()
|
|
65
|
+
env:
|
|
66
|
+
QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
|
|
67
|
+
QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
|
|
68
|
+
run: |
|
|
69
|
+
APP_NAME="${{ matrix.app }}"
|
|
70
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
71
|
+
REPORT_KEY="$(echo "$APP_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')_coverage"
|
|
72
|
+
|
|
73
|
+
if [ -d coverage ]; then
|
|
74
|
+
if [ -z "$QUALITY_DASHBOARD_URL" ] || [ -z "$QUALITY_DASHBOARD_TOKEN" ]; then
|
|
75
|
+
echo "Quality Dashboard URL or token not set, skipping upload"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
# Zip coverage contents at root level so the jest processor
|
|
79
|
+
# finds coverage-final.json / clover.xml directly
|
|
80
|
+
(cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
|
|
81
|
+
|
|
82
|
+
META=$(jq -n \
|
|
83
|
+
--arg key "$REPORT_KEY" \
|
|
84
|
+
--arg pkg "$PACKAGE_NAME" \
|
|
85
|
+
'{
|
|
86
|
+
key: $key,
|
|
87
|
+
displayName: "Coverage: \($pkg)",
|
|
88
|
+
processor: "jest"
|
|
89
|
+
}')
|
|
90
|
+
|
|
91
|
+
echo "Uploading coverage: $REPORT_KEY"
|
|
92
|
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
93
|
+
-X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
|
|
94
|
+
-H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
|
|
95
|
+
-F "meta=$META" \
|
|
96
|
+
-F "file=@${{ runner.temp }}/coverage.zip" \
|
|
97
|
+
--max-time 30)
|
|
98
|
+
|
|
99
|
+
if [ "$HTTP_CODE" = "201" ]; then
|
|
100
|
+
echo "\u2713 Coverage uploaded ($HTTP_CODE)"
|
|
101
|
+
else
|
|
102
|
+
echo "\u26a0 Upload returned HTTP $HTTP_CODE"
|
|
103
|
+
fi
|
|
104
|
+
else
|
|
105
|
+
echo "No coverage/ directory found, skipping upload"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
docker-build:
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/checkout@v6
|
|
112
|
+
|
|
113
|
+
- name: Set up QEMU
|
|
114
|
+
uses: docker/setup-qemu-action@v4
|
|
115
|
+
|
|
116
|
+
- name: Set up Docker Buildx
|
|
117
|
+
uses: docker/setup-buildx-action@v4
|
|
118
|
+
|
|
119
|
+
- name: Login to Docker Hub
|
|
120
|
+
uses: docker/login-action@v4
|
|
121
|
+
with:
|
|
122
|
+
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
|
123
|
+
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
|
124
|
+
|
|
125
|
+
- name: Build and Push Docker Image
|
|
126
|
+
run: |
|
|
127
|
+
set -e
|
|
128
|
+
SERVICE_NAME=$(cat package.json | jq -r '.name')
|
|
129
|
+
echo "Building ${SERVICE_NAME}:beta"
|
|
130
|
+
docker buildx build \
|
|
131
|
+
--platform ${{ inputs.docker_platforms }} \
|
|
132
|
+
--push \
|
|
133
|
+
-f Dockerfile \
|
|
134
|
+
-t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:beta \
|
|
135
|
+
.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# common-utils
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./src/OTelContext";
|
|
2
|
+
export * from "./src/ConfigBase";
|
|
3
|
+
export * from "./src/DbUtils";
|
|
4
|
+
export * from "./src/DbUtilsNoTelemetry";
|
|
5
|
+
export * from "./src/SqlDbUtils";
|
|
6
|
+
export * from "./src/PostgresDbUtils";
|
|
7
|
+
export * from "./src/SystemCommand";
|
|
8
|
+
export * from "./src/Timeout";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./src/OTelContext"), exports);
|
|
18
|
+
__exportStar(require("./src/ConfigBase"), exports);
|
|
19
|
+
__exportStar(require("./src/DbUtils"), exports);
|
|
20
|
+
__exportStar(require("./src/DbUtilsNoTelemetry"), exports);
|
|
21
|
+
__exportStar(require("./src/SqlDbUtils"), exports);
|
|
22
|
+
__exportStar(require("./src/PostgresDbUtils"), exports);
|
|
23
|
+
__exportStar(require("./src/SystemCommand"), exports);
|
|
24
|
+
__exportStar(require("./src/Timeout"), exports);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ConfigOTelInterface } from "@devopsplaybook.io/otel-utils";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration field descriptor used by {@link ConfigBase.addConfigField}.
|
|
4
|
+
*/
|
|
5
|
+
export interface ConfigFieldDef {
|
|
6
|
+
/** Property name on the config instance. */
|
|
7
|
+
field: string;
|
|
8
|
+
/** When `true` the value is masked in log output. */
|
|
9
|
+
sensitive?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Database-specific configuration fields shared by every project that
|
|
13
|
+
* supports both SQLite and PostgreSQL backends.
|
|
14
|
+
*/
|
|
15
|
+
export interface ConfigDatabaseInterface {
|
|
16
|
+
DATABASE_TYPE: "sqlite" | "postgres";
|
|
17
|
+
DATABASE_POSTGRES_HOST: string;
|
|
18
|
+
DATABASE_POSTGRES_PORT: number;
|
|
19
|
+
DATABASE_POSTGRES_USER: string;
|
|
20
|
+
DATABASE_POSTGRES_PASSWORD: string;
|
|
21
|
+
DATABASE_POSTGRES_DATABASE: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Common server configuration fields shared across projects.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConfigCommonInterface extends ConfigOTelInterface, ConfigDatabaseInterface {
|
|
27
|
+
CONFIG_FILE: string;
|
|
28
|
+
API_PORT: number;
|
|
29
|
+
JWT_VALIDITY_DURATION: number;
|
|
30
|
+
CORS_POLICY_ORIGIN: string;
|
|
31
|
+
DATA_DIR: string;
|
|
32
|
+
JWT_KEY: string;
|
|
33
|
+
LOG_LEVEL: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Abstract base class for project configuration.
|
|
37
|
+
*
|
|
38
|
+
* Implements the three-layer override strategy used across all
|
|
39
|
+
* devopsplaybook.io server projects:
|
|
40
|
+
* 1. **Environment variable** (highest priority)
|
|
41
|
+
* 2. **config.json** file value
|
|
42
|
+
* 3. **Default** declared on the class property
|
|
43
|
+
*
|
|
44
|
+
* Subclasses add project-specific fields and call {@link addConfigField}
|
|
45
|
+
* inside their constructor so that {@link reload} picks them up.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* class MyConfig extends ConfigBase {
|
|
50
|
+
* public MY_SETTING = "default";
|
|
51
|
+
* constructor() {
|
|
52
|
+
* super("my-service");
|
|
53
|
+
* this.addConfigField({ field: "MY_SETTING" });
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare abstract class ConfigBase implements ConfigCommonInterface {
|
|
59
|
+
SERVICE_ID: string;
|
|
60
|
+
VERSION: string;
|
|
61
|
+
OPENTELEMETRY_COLLECTOR_HTTP_TRACES: string;
|
|
62
|
+
OPENTELEMETRY_COLLECTOR_HTTP_METRICS: string;
|
|
63
|
+
OPENTELEMETRY_COLLECTOR_HTTP_LOGS: string;
|
|
64
|
+
OPENTELEMETRY_COLLECTOR_AWS: boolean;
|
|
65
|
+
OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS: number;
|
|
66
|
+
OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS: number;
|
|
67
|
+
OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER: string;
|
|
68
|
+
CONFIG_FILE: string;
|
|
69
|
+
API_PORT: number;
|
|
70
|
+
JWT_VALIDITY_DURATION: number;
|
|
71
|
+
CORS_POLICY_ORIGIN: string;
|
|
72
|
+
DATA_DIR: string;
|
|
73
|
+
JWT_KEY: string;
|
|
74
|
+
LOG_LEVEL: string;
|
|
75
|
+
DATABASE_TYPE: "sqlite" | "postgres";
|
|
76
|
+
DATABASE_POSTGRES_HOST: string;
|
|
77
|
+
DATABASE_POSTGRES_PORT: number;
|
|
78
|
+
DATABASE_POSTGRES_USER: string;
|
|
79
|
+
DATABASE_POSTGRES_PASSWORD: string;
|
|
80
|
+
DATABASE_POSTGRES_DATABASE: string;
|
|
81
|
+
/**
|
|
82
|
+
* Fields registered by subclasses (or the base) that {@link reload}
|
|
83
|
+
* should process. Common / DB / OTel fields are pre-registered.
|
|
84
|
+
*/
|
|
85
|
+
private _fields;
|
|
86
|
+
/**
|
|
87
|
+
* @param serviceId Unique service identifier (e.g. `"cryptotrader-server"`).
|
|
88
|
+
* @param configFile Optional path to the JSON config file. Defaults to `"config.json"`.
|
|
89
|
+
*/
|
|
90
|
+
constructor(serviceId: string, configFile?: string);
|
|
91
|
+
/**
|
|
92
|
+
* Register a configuration field so that {@link reload} processes it.
|
|
93
|
+
* Call this in your subclass constructor for every project-specific field.
|
|
94
|
+
*/
|
|
95
|
+
addConfigField(def: ConfigFieldDef): void;
|
|
96
|
+
/**
|
|
97
|
+
* Load (or reload) configuration from the JSON file and environment variables.
|
|
98
|
+
* Environment variables always take precedence over file values.
|
|
99
|
+
*
|
|
100
|
+
* @param logger Optional log callback `(message: string) => void`.
|
|
101
|
+
* When omitted nothing is logged (useful in tests).
|
|
102
|
+
*/
|
|
103
|
+
reload(logger?: (message: string) => void): Promise<void>;
|
|
104
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ConfigBase = void 0;
|
|
40
|
+
const fse = __importStar(require("fs-extra"));
|
|
41
|
+
const uuid_1 = require("uuid");
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Abstract base class for project configuration.
|
|
45
|
+
*
|
|
46
|
+
* Implements the three-layer override strategy used across all
|
|
47
|
+
* devopsplaybook.io server projects:
|
|
48
|
+
* 1. **Environment variable** (highest priority)
|
|
49
|
+
* 2. **config.json** file value
|
|
50
|
+
* 3. **Default** declared on the class property
|
|
51
|
+
*
|
|
52
|
+
* Subclasses add project-specific fields and call {@link addConfigField}
|
|
53
|
+
* inside their constructor so that {@link reload} picks them up.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* class MyConfig extends ConfigBase {
|
|
58
|
+
* public MY_SETTING = "default";
|
|
59
|
+
* constructor() {
|
|
60
|
+
* super("my-service");
|
|
61
|
+
* this.addConfigField({ field: "MY_SETTING" });
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
class ConfigBase {
|
|
67
|
+
/**
|
|
68
|
+
* @param serviceId Unique service identifier (e.g. `"cryptotrader-server"`).
|
|
69
|
+
* @param configFile Optional path to the JSON config file. Defaults to `"config.json"`.
|
|
70
|
+
*/
|
|
71
|
+
constructor(serviceId, configFile) {
|
|
72
|
+
this.VERSION = "1";
|
|
73
|
+
this.OPENTELEMETRY_COLLECTOR_HTTP_TRACES = "";
|
|
74
|
+
this.OPENTELEMETRY_COLLECTOR_HTTP_METRICS = "";
|
|
75
|
+
this.OPENTELEMETRY_COLLECTOR_HTTP_LOGS = "";
|
|
76
|
+
this.OPENTELEMETRY_COLLECTOR_AWS = false;
|
|
77
|
+
this.OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS = 60;
|
|
78
|
+
this.OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS = 60;
|
|
79
|
+
this.OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER = "";
|
|
80
|
+
this.API_PORT = 8080;
|
|
81
|
+
this.JWT_VALIDITY_DURATION = 3 * 31 * 24 * 3600;
|
|
82
|
+
this.CORS_POLICY_ORIGIN = "";
|
|
83
|
+
this.DATA_DIR = process.env.DATA_DIR || "/data";
|
|
84
|
+
this.JWT_KEY = (0, uuid_1.v4)();
|
|
85
|
+
this.LOG_LEVEL = "info";
|
|
86
|
+
// -- Database fields --
|
|
87
|
+
this.DATABASE_TYPE = "sqlite";
|
|
88
|
+
this.DATABASE_POSTGRES_HOST = "";
|
|
89
|
+
this.DATABASE_POSTGRES_PORT = 5432;
|
|
90
|
+
this.DATABASE_POSTGRES_USER = "";
|
|
91
|
+
this.DATABASE_POSTGRES_PASSWORD = "";
|
|
92
|
+
this.DATABASE_POSTGRES_DATABASE = "";
|
|
93
|
+
/**
|
|
94
|
+
* Fields registered by subclasses (or the base) that {@link reload}
|
|
95
|
+
* should process. Common / DB / OTel fields are pre-registered.
|
|
96
|
+
*/
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
this._fields = [];
|
|
99
|
+
this.SERVICE_ID = serviceId;
|
|
100
|
+
this.CONFIG_FILE = configFile || process.env.CONFIG_FILE || "config.json";
|
|
101
|
+
// Auto-detect version from nearest package.json
|
|
102
|
+
try {
|
|
103
|
+
const pkg = fse.readJsonSync(path_1.default.resolve(__dirname, "../package.json"));
|
|
104
|
+
if (pkg && pkg.version) {
|
|
105
|
+
this.VERSION = pkg.version;
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
108
|
+
}
|
|
109
|
+
catch (_e) {
|
|
110
|
+
// fallback to "1"
|
|
111
|
+
}
|
|
112
|
+
// Pre-register base + DB + OTel fields so reload() handles them
|
|
113
|
+
const baseFields = [
|
|
114
|
+
{ field: "JWT_VALIDITY_DURATION" },
|
|
115
|
+
{ field: "CORS_POLICY_ORIGIN" },
|
|
116
|
+
{ field: "DATA_DIR" },
|
|
117
|
+
{ field: "JWT_KEY", sensitive: true },
|
|
118
|
+
{ field: "LOG_LEVEL" },
|
|
119
|
+
{ field: "DATABASE_TYPE" },
|
|
120
|
+
{ field: "DATABASE_POSTGRES_HOST" },
|
|
121
|
+
{ field: "DATABASE_POSTGRES_PORT" },
|
|
122
|
+
{ field: "DATABASE_POSTGRES_USER" },
|
|
123
|
+
{ field: "DATABASE_POSTGRES_PASSWORD", sensitive: true },
|
|
124
|
+
{ field: "DATABASE_POSTGRES_DATABASE" },
|
|
125
|
+
{ field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
|
|
126
|
+
{ field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
|
|
127
|
+
{ field: "OPENTELEMETRY_COLLECTOR_HTTP_LOGS" },
|
|
128
|
+
{ field: "OPENTELEMETRY_COLLECTOR_AWS" },
|
|
129
|
+
{
|
|
130
|
+
field: "OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
field: "OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
field: "OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER",
|
|
137
|
+
sensitive: true,
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
for (const f of baseFields) {
|
|
141
|
+
this.addConfigField(f);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Register a configuration field so that {@link reload} processes it.
|
|
146
|
+
* Call this in your subclass constructor for every project-specific field.
|
|
147
|
+
*/
|
|
148
|
+
addConfigField(def) {
|
|
149
|
+
var _a;
|
|
150
|
+
this._fields.push({
|
|
151
|
+
field: def.field,
|
|
152
|
+
sensitive: (_a = def.sensitive) !== null && _a !== void 0 ? _a : false,
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
154
|
+
defaultValue: this[def.field],
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Load (or reload) configuration from the JSON file and environment variables.
|
|
159
|
+
* Environment variables always take precedence over file values.
|
|
160
|
+
*
|
|
161
|
+
* @param logger Optional log callback `(message: string) => void`.
|
|
162
|
+
* When omitted nothing is logged (useful in tests).
|
|
163
|
+
*/
|
|
164
|
+
async reload(logger) {
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
166
|
+
const log = logger !== null && logger !== void 0 ? logger : (() => { });
|
|
167
|
+
let content = {};
|
|
168
|
+
try {
|
|
169
|
+
content = await fse.readJson(this.CONFIG_FILE);
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
171
|
+
}
|
|
172
|
+
catch (_e) {
|
|
173
|
+
// config file is optional – fall back to env + defaults
|
|
174
|
+
}
|
|
175
|
+
log(`Configuration Value: CONFIG_FILE: ${this.CONFIG_FILE}`);
|
|
176
|
+
log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
|
|
177
|
+
log(`Configuration Value: VERSION: ${this.VERSION}`);
|
|
178
|
+
for (const { field, sensitive } of this._fields) {
|
|
179
|
+
let from = "defaults";
|
|
180
|
+
if (process.env[field] !== undefined) {
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
+
this[field] = process.env[field];
|
|
183
|
+
from = "environment";
|
|
184
|
+
}
|
|
185
|
+
else if (content[field] !== undefined) {
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
187
|
+
this[field] = content[field];
|
|
188
|
+
from = "config";
|
|
189
|
+
}
|
|
190
|
+
if (sensitive) {
|
|
191
|
+
log(`Configuration Value: ${field}: ******************** (from ${from})`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
log(
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
|
+
`Configuration Value: ${field}: ${this[field]} (from ${from})`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.ConfigBase = ConfigBase;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Span } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
import { StandardTracer, StandardLogger } from "@devopsplaybook.io/otel-utils";
|
|
3
|
+
import * as SqlDbUtils from "./SqlDbUtils";
|
|
4
|
+
import * as PostgresDbUtils from "./PostgresDbUtils";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration subset required by the unified DB facade.
|
|
7
|
+
*/
|
|
8
|
+
export interface DbUtilsConfig extends SqlDbUtils.SqlDbConfig, PostgresDbUtils.PostgresDbConfig {
|
|
9
|
+
DATABASE_TYPE: "sqlite" | "postgres";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Injects the OTel tracer and logger instances used by the DB layer.
|
|
13
|
+
* Must be called once at startup, before {@link DbUtilsInit}.
|
|
14
|
+
*/
|
|
15
|
+
export declare function DbUtilsSetOTel(tracer: StandardTracer, logger: StandardLogger): void;
|
|
16
|
+
/**
|
|
17
|
+
* Initialise the database layer.
|
|
18
|
+
*
|
|
19
|
+
* Dispatches to the SQLite or Postgres backend depending on
|
|
20
|
+
* `config.DATABASE_TYPE` and runs pending migration files from `sqlDir`.
|
|
21
|
+
*
|
|
22
|
+
* @param context Parent OTel span.
|
|
23
|
+
* @param config Server configuration.
|
|
24
|
+
* @param sqlDir Absolute path to the directory containing SQL migration files.
|
|
25
|
+
*/
|
|
26
|
+
export declare function DbUtilsInit(context: Span, config: DbUtilsConfig, sqlDir: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the native database handle.
|
|
29
|
+
* - SQLite: `better-sqlite3` `Database` instance
|
|
30
|
+
* - Postgres: `pg` `Pool` instance
|
|
31
|
+
*/
|
|
32
|
+
export declare function DbUtilsGetDatabase(): any;
|
|
33
|
+
/** Convert SQLite `?` placeholders to PostgreSQL `$1, $2, ...` numbering. */
|
|
34
|
+
export declare function convertToPostgresPlaceholders(sql: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a write SQL statement with OTel tracing.
|
|
37
|
+
* Automatically converts `?` placeholders to `$N` when using Postgres.
|
|
38
|
+
*
|
|
39
|
+
* @returns Number of rows changed.
|
|
40
|
+
*/
|
|
41
|
+
export declare function DbUtilsExecSQL(context: Span, sql: string, params?: unknown[]): number | Promise<number>;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a read SQL query with OTel tracing.
|
|
44
|
+
* Automatically converts `?` placeholders to `$N` when using Postgres.
|
|
45
|
+
*
|
|
46
|
+
* @returns Array of row objects.
|
|
47
|
+
*/
|
|
48
|
+
export declare function DbUtilsQuerySQL(context: Span, sql: string, params?: unknown[], debug?: boolean): any[] | Promise<any[]>;
|
|
49
|
+
/** Returns the active database type (`"sqlite"` or `"postgres"`). */
|
|
50
|
+
export declare function DbUtilsGetType(): "sqlite" | "postgres";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DbUtilsSetOTel = DbUtilsSetOTel;
|
|
37
|
+
exports.DbUtilsInit = DbUtilsInit;
|
|
38
|
+
exports.DbUtilsGetDatabase = DbUtilsGetDatabase;
|
|
39
|
+
exports.convertToPostgresPlaceholders = convertToPostgresPlaceholders;
|
|
40
|
+
exports.DbUtilsExecSQL = DbUtilsExecSQL;
|
|
41
|
+
exports.DbUtilsQuerySQL = DbUtilsQuerySQL;
|
|
42
|
+
exports.DbUtilsGetType = DbUtilsGetType;
|
|
43
|
+
const SqlDbUtils = __importStar(require("./SqlDbUtils"));
|
|
44
|
+
const PostgresDbUtils = __importStar(require("./PostgresDbUtils"));
|
|
45
|
+
let databaseType = "sqlite";
|
|
46
|
+
/**
|
|
47
|
+
* Injects the OTel tracer and logger instances used by the DB layer.
|
|
48
|
+
* Must be called once at startup, before {@link DbUtilsInit}.
|
|
49
|
+
*/
|
|
50
|
+
function DbUtilsSetOTel(tracer, logger) {
|
|
51
|
+
SqlDbUtils.SqlDbUtilsSetOTel(tracer, logger);
|
|
52
|
+
PostgresDbUtils.PostgresDbUtilsSetOTel(tracer, logger);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initialise the database layer.
|
|
56
|
+
*
|
|
57
|
+
* Dispatches to the SQLite or Postgres backend depending on
|
|
58
|
+
* `config.DATABASE_TYPE` and runs pending migration files from `sqlDir`.
|
|
59
|
+
*
|
|
60
|
+
* @param context Parent OTel span.
|
|
61
|
+
* @param config Server configuration.
|
|
62
|
+
* @param sqlDir Absolute path to the directory containing SQL migration files.
|
|
63
|
+
*/
|
|
64
|
+
async function DbUtilsInit(context, config, sqlDir) {
|
|
65
|
+
databaseType = config.DATABASE_TYPE;
|
|
66
|
+
if (databaseType === "postgres") {
|
|
67
|
+
await PostgresDbUtils.PostgresDbUtilsInit(context, config, sqlDir);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
await SqlDbUtils.SqlDbUtilsInit(context, config, sqlDir);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns the native database handle.
|
|
75
|
+
* - SQLite: `better-sqlite3` `Database` instance
|
|
76
|
+
* - Postgres: `pg` `Pool` instance
|
|
77
|
+
*/
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
function DbUtilsGetDatabase() {
|
|
80
|
+
if (databaseType === "postgres") {
|
|
81
|
+
return PostgresDbUtils.PostgresDbUtilsGetPool();
|
|
82
|
+
}
|
|
83
|
+
return SqlDbUtils.SqlDbUtilsGetDatabase();
|
|
84
|
+
}
|
|
85
|
+
/** Convert SQLite `?` placeholders to PostgreSQL `$1, $2, ...` numbering. */
|
|
86
|
+
function convertToPostgresPlaceholders(sql) {
|
|
87
|
+
let paramIndex = 1;
|
|
88
|
+
return sql.replace(/\?/g, () => `$${paramIndex++}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Execute a write SQL statement with OTel tracing.
|
|
92
|
+
* Automatically converts `?` placeholders to `$N` when using Postgres.
|
|
93
|
+
*
|
|
94
|
+
* @returns Number of rows changed.
|
|
95
|
+
*/
|
|
96
|
+
function DbUtilsExecSQL(context, sql, params = []) {
|
|
97
|
+
if (databaseType === "postgres") {
|
|
98
|
+
return PostgresDbUtils.PostgresDbUtilsExecSQL(context, convertToPostgresPlaceholders(sql), params);
|
|
99
|
+
}
|
|
100
|
+
return SqlDbUtils.SqlDbUtilsExecSQL(context, sql, params);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Execute a read SQL query with OTel tracing.
|
|
104
|
+
* Automatically converts `?` placeholders to `$N` when using Postgres.
|
|
105
|
+
*
|
|
106
|
+
* @returns Array of row objects.
|
|
107
|
+
*/
|
|
108
|
+
function DbUtilsQuerySQL(context, sql, params = [], debug = false) {
|
|
109
|
+
if (databaseType === "postgres") {
|
|
110
|
+
return PostgresDbUtils.PostgresDbUtilsQuerySQL(context, convertToPostgresPlaceholders(sql), params, debug);
|
|
111
|
+
}
|
|
112
|
+
return SqlDbUtils.SqlDbUtilsQuerySQL(context, sql, params, debug);
|
|
113
|
+
}
|
|
114
|
+
/** Returns the active database type (`"sqlite"` or `"postgres"`). */
|
|
115
|
+
function DbUtilsGetType() {
|
|
116
|
+
return databaseType;
|
|
117
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StandardLogger } from "@devopsplaybook.io/otel-utils";
|
|
2
|
+
/**
|
|
3
|
+
* Injects the OTel logger instance used by no-telemetry DB operations.
|
|
4
|
+
* Must be called once at startup.
|
|
5
|
+
*/
|
|
6
|
+
export declare function DbUtilsNoTelemetrySetLogger(loggerIn: StandardLogger): void;
|
|
7
|
+
/**
|
|
8
|
+
* Execute a multi-row INSERT with a flat parameter array.
|
|
9
|
+
* Builds: INSERT INTO <tableCols> VALUES (?,?...),(?,?...),...
|
|
10
|
+
*
|
|
11
|
+
* @returns Number of rows inserted.
|
|
12
|
+
*/
|
|
13
|
+
export declare function DbUtilsNoTelemetryBatchInsert(tableCols: string, numCols: number, rows: any[][]): number | Promise<number>;
|
|
14
|
+
/**
|
|
15
|
+
* Execute a write SQL statement **without** creating an OTel span.
|
|
16
|
+
* Use this on high-throughput paths where span overhead matters.
|
|
17
|
+
*
|
|
18
|
+
* @returns Number of rows changed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function DbUtilsNoTelemetryExecSQL(sql: string, params?: unknown[]): number | Promise<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Execute a read SQL query **without** creating an OTel span.
|
|
23
|
+
* Use this on high-throughput paths where span overhead matters.
|
|
24
|
+
*
|
|
25
|
+
* @returns Array of row objects.
|
|
26
|
+
*/
|
|
27
|
+
export declare function DbUtilsNoTelemetryQuerySQL(sql: string, params?: unknown[], debug?: boolean): any[] | Promise<any[]>;
|