From 89ecf032d68748cb5da5e9168352ac550737aaf6 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 9 Sep 2024 14:45:17 -0500 Subject: [PATCH 01/24] handle all manifests in k8s --- .github/shared/kubernetes/apply/action.yml | 43 ++++++++-------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/.github/shared/kubernetes/apply/action.yml b/.github/shared/kubernetes/apply/action.yml index cd3b2ae..7545a91 100644 --- a/.github/shared/kubernetes/apply/action.yml +++ b/.github/shared/kubernetes/apply/action.yml @@ -19,41 +19,30 @@ runs: #### - - name: Apply configmap - if: ${{ hashFiles('k8s/configmap.yaml') != '' }} - run: kubectl apply -f k8s/configmap.yaml ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} - shell: bash - - - name: Describe configmap - if: ${{ hashFiles('k8s/configmap.yaml') != '' && inputs.dry_run != 'true' && inputs.debug == 'true' }} - run: kubectl describe cm $(yq -r .metadata.name k8s/configmap.yaml) + - name: Apply all manifests + run: | + for file in k8s/*.yaml k8s/*.yml; do + kubectl apply -f $file ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + done shell: bash #### - - name: Apply deployment - if: ${{ hashFiles('k8s/deployment.yaml') != '' }} - run: kubectl apply -f k8s/deployment.yaml ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + - name: Describe resources + if: ${{ inputs.debug == 'true' && inputs.dry_run != 'true' }} + run: | + for file in k8s/*.yaml k8s/*.yml; do + kind=$(yq -r .kind $file) + name=$(yq -r .metadata.name $file) + kubectl describe $kind $name + done shell: bash - - name: Describe deployment - if: ${{ hashFiles('k8s/deployment.yaml') != '' && inputs.dry_run != 'true' && inputs.debug == 'true' }} - run: kubectl describe deploy $(yq -r .metadata.name k8s/deployment.yaml) - shell: bash + #### - name: Check deployment rollout status - if: ${{ hashFiles('k8s/deployment.yaml') != '' && inputs.dry_run != 'true' }} + if: ${{ (hashFiles('k8s/deployment.yaml') != '' || hashFiles('k8s/deployment.yml') != '') && inputs.dry_run != 'true' }} run: kubectl rollout status deploy $(yq -r .metadata.name k8s/deployment.yaml) shell: bash - #### - - - name: Apply service - if: ${{ hashFiles('k8s/service.yaml') != '' }} - run: kubectl apply -f k8s/service.yaml ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} - shell: bash - - - name: Describe service - if: ${{ hashFiles('k8s/service.yaml') != '' && inputs.dry_run != 'true' && inputs.debug == 'true' }} - run: kubectl describe svc $(yq -r .metadata.name k8s/service.yaml) - shell: bash \ No newline at end of file + #### \ No newline at end of file From 655c607dba01a97120062d30f743061e2f42d9b5 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 10 Sep 2024 12:26:06 -0500 Subject: [PATCH 02/24] Update action.yml to handle "missing" * files --- .github/shared/kubernetes/apply/action.yml | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/shared/kubernetes/apply/action.yml b/.github/shared/kubernetes/apply/action.yml index 7545a91..d91dd8f 100644 --- a/.github/shared/kubernetes/apply/action.yml +++ b/.github/shared/kubernetes/apply/action.yml @@ -22,7 +22,9 @@ runs: - name: Apply all manifests run: | for file in k8s/*.yaml k8s/*.yml; do - kubectl apply -f $file ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + if [ -e "$file" ]; then + kubectl apply -f $file ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + fi done shell: bash @@ -32,9 +34,11 @@ runs: if: ${{ inputs.debug == 'true' && inputs.dry_run != 'true' }} run: | for file in k8s/*.yaml k8s/*.yml; do - kind=$(yq -r .kind $file) - name=$(yq -r .metadata.name $file) - kubectl describe $kind $name + if [ -e "$file" ]; then + kind=$(yq -r .kind $file) + name=$(yq -r .metadata.name $file) + kubectl describe $kind $name + fi done shell: bash @@ -42,7 +46,14 @@ runs: - name: Check deployment rollout status if: ${{ (hashFiles('k8s/deployment.yaml') != '' || hashFiles('k8s/deployment.yml') != '') && inputs.dry_run != 'true' }} - run: kubectl rollout status deploy $(yq -r .metadata.name k8s/deployment.yaml) + run: | + if [ -f k8s/deployment.yaml ]; then + kubectl rollout status deploy $(yq -r .metadata.name k8s/deployment.yaml) + elif [ -f k8s/deployment.yml ]; then + kubectl rollout status deploy $(yq -r .metadata.name k8s/deployment.yml) + else + echo "No deployment file found." + fi shell: bash - #### \ No newline at end of file + #### From 42ea9445f85a4ad7138e06586c7654e63c5b8239 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 12 Sep 2024 15:03:57 -0500 Subject: [PATCH 03/24] Update action.yml to sanitize inputs --- .../kubernetes/manifests/configmap/action.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/shared/kubernetes/manifests/configmap/action.yml b/.github/shared/kubernetes/manifests/configmap/action.yml index dff81e9..6467c2b 100644 --- a/.github/shared/kubernetes/manifests/configmap/action.yml +++ b/.github/shared/kubernetes/manifests/configmap/action.yml @@ -19,12 +19,12 @@ runs: IFS=',' read -ra env_files <<< "${{ inputs.env_files }}" output="" for env_file in "${env_files[@]}"; do - trimmed_env_file=$(echo "$env_file" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + trimmed_env_file=$(echo "$env_file" | sed 's|^[[:space:]]*||;s|[[:space:]]*$||' | sed -e 's|[&|]|\\&|g') if [ -f "$trimmed_env_file" ]; then output+="--from-env-file $trimmed_env_file " fi done - output=$(echo "$output" | sed 's/ $//') + output=$(echo "$output" | sed 's| $||') echo "from_files=$output" >> "$GITHUB_OUTPUT" shell: bash @@ -53,18 +53,18 @@ runs: run: | regex="@@(.+)@@" while read -r line ; do - if [[ $line =~ $regex ]] ; then - echo "Found: $line" - export VARIABLE_NAME=${BASH_REMATCH[1]} - echo "Env variable name: $VARIABLE_NAME" - echo "Value of variable: $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" - echo "Replacing @@$VARIABLE_NAME@@ with $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" - sed -i "s|@@$VARIABLE_NAME@@|$(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)|" k8s/configmap.yaml - fi + if [[ $line =~ $regex ]] ; then + echo "Found: $line" + export VARIABLE_NAME=${BASH_REMATCH[1]} + echo "Env variable name: $VARIABLE_NAME" + echo "Value of variable: $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" + echo "Replacing @@$VARIABLE_NAME@@ with $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" + sed -i "s|@@$VARIABLE_NAME@@|$(echo $GH_SECRETS | jq -r .$VARIABLE_NAME | sed -e 's|[&/\]|\\&|g')|" k8s/configmap.yaml + fi done < k8s/configmap.yaml shell: bash - name: Display configmap if: ${{ hashFiles('k8s/configmap.yaml') != '' && inputs.debug == 'true' }} run: cat k8s/configmap.yaml - shell: bash \ No newline at end of file + shell: bash From 07f76b887e2f82b90927dfb3869f32a392191b20 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 12 Sep 2024 15:38:07 -0500 Subject: [PATCH 04/24] Update action.yml to include more sanitation --- .github/shared/kubernetes/manifests/configmap/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/shared/kubernetes/manifests/configmap/action.yml b/.github/shared/kubernetes/manifests/configmap/action.yml index 6467c2b..bea7138 100644 --- a/.github/shared/kubernetes/manifests/configmap/action.yml +++ b/.github/shared/kubernetes/manifests/configmap/action.yml @@ -19,7 +19,7 @@ runs: IFS=',' read -ra env_files <<< "${{ inputs.env_files }}" output="" for env_file in "${env_files[@]}"; do - trimmed_env_file=$(echo "$env_file" | sed 's|^[[:space:]]*||;s|[[:space:]]*$||' | sed -e 's|[&|]|\\&|g') + trimmed_env_file=$(echo "$env_file" | sed 's|^[[:space:]]*||;s|[[:space:]]*$||' | sed -e 's|[&|$]|\\&|g') if [ -f "$trimmed_env_file" ]; then output+="--from-env-file $trimmed_env_file " fi @@ -59,7 +59,7 @@ runs: echo "Env variable name: $VARIABLE_NAME" echo "Value of variable: $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" echo "Replacing @@$VARIABLE_NAME@@ with $(echo $GH_SECRETS | jq -r .$VARIABLE_NAME)" - sed -i "s|@@$VARIABLE_NAME@@|$(echo $GH_SECRETS | jq -r .$VARIABLE_NAME | sed -e 's|[&/\]|\\&|g')|" k8s/configmap.yaml + sed -i "s|@@$VARIABLE_NAME@@|$(echo $GH_SECRETS | jq -r .$VARIABLE_NAME | sed -e 's|[&|$.*^/\]|\\&|g')|" k8s/configmap.yaml fi done < k8s/configmap.yaml shell: bash From 0eb7092a43bf0331aa492a608b2493d631956e8f Mon Sep 17 00:00:00 2001 From: Jimmy Date: Fri, 13 Sep 2024 09:16:33 -0500 Subject: [PATCH 05/24] Update action.yml to handle restarting resources in k8s when re-running jobs --- .github/shared/kubernetes/apply/action.yml | 32 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/shared/kubernetes/apply/action.yml b/.github/shared/kubernetes/apply/action.yml index d91dd8f..87f43ca 100644 --- a/.github/shared/kubernetes/apply/action.yml +++ b/.github/shared/kubernetes/apply/action.yml @@ -19,11 +19,35 @@ runs: #### + - name: Dry run all manifests + if: ${{ inputs.dry_run == 'true' }} + run: | + for file in k8s/*.yaml k8s/*.yml; do + if [ -e "$file" ]; then + kubectl apply -f "$file" ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + fi + done + shell: bash + - name: Apply all manifests + if: ${{ inputs.dry_run != 'true' }} run: | for file in k8s/*.yaml k8s/*.yml; do if [ -e "$file" ]; then - kubectl apply -f $file ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + if kubectl diff -f "$file" > /dev/null; then + kind=$(yq -r .kind "$file" | tr '[:upper:]' '[:lower:]') + case "$kind" in + deployment|statefulset|daemonset) + name=$(yq -r .metadata.name "$file") + kubectl rollout restart "$kind" "$name" + ;; + *) + echo "Resource type $kind does not support rollout restart." + ;; + esac + else + kubectl apply -f "$file" + fi fi done shell: bash @@ -35,9 +59,9 @@ runs: run: | for file in k8s/*.yaml k8s/*.yml; do if [ -e "$file" ]; then - kind=$(yq -r .kind $file) - name=$(yq -r .metadata.name $file) - kubectl describe $kind $name + kind=$(yq -r .kind "$file") + name=$(yq -r .metadata.name "$file") + kubectl describe "$kind" "$name" fi done shell: bash From f95abc8cf59e6723278115b49810d427895f9036 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 17 Sep 2024 12:26:55 -0500 Subject: [PATCH 06/24] Update action.yml to only allow non-dry-run in production --- .github/shared/kubernetes/apply/action.yml | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/shared/kubernetes/apply/action.yml b/.github/shared/kubernetes/apply/action.yml index 87f43ca..cc2868d 100644 --- a/.github/shared/kubernetes/apply/action.yml +++ b/.github/shared/kubernetes/apply/action.yml @@ -12,25 +12,43 @@ inputs: runs: using: composite steps: + + - name: Check branch + id: check_branch + run: echo "branch_name=$(echo $GITHUB_REF | sed 's|refs/heads/||')" >> $GITHUB_ENV + shell: bash + + - name: Validate dry_run usage + id: if_production + run: | + if [[ "$branch_name" == "main" || "$branch_name" == "master" ]]; then + echo "dry_run=${{ inputs.dry_run }}" >> "$GITHUB_OUTPUT" + else + echo "dry_run=true" >> "$GITHUB_OUTPUT" + fi + shell: bash + + #### + - name: Test kubectl access - if: ${{ inputs.debug == 'true' }} + if: ${{ steps.if_production.outputs.dry_run != 'true' && inputs.debug == 'true' }} run: kubectl get pods shell: bash #### - name: Dry run all manifests - if: ${{ inputs.dry_run == 'true' }} + if: ${{ steps.if_production.outputs.dry_run == 'true' }} run: | for file in k8s/*.yaml k8s/*.yml; do if [ -e "$file" ]; then - kubectl apply -f "$file" ${{ inputs.dry_run == 'true' && '--dry-run=client' || '' }} + kubectl apply -f "$file" ${{ steps.if_production.outputs.dry_run == 'true' && '--dry-run=client' || '' }} fi done shell: bash - name: Apply all manifests - if: ${{ inputs.dry_run != 'true' }} + if: ${{ steps.if_production.outputs.dry_run != 'true' }} run: | for file in k8s/*.yaml k8s/*.yml; do if [ -e "$file" ]; then @@ -55,7 +73,7 @@ runs: #### - name: Describe resources - if: ${{ inputs.debug == 'true' && inputs.dry_run != 'true' }} + if: ${{ steps.if_production.outputs.dry_run != 'true' && inputs.debug == 'true' }} run: | for file in k8s/*.yaml k8s/*.yml; do if [ -e "$file" ]; then @@ -69,7 +87,7 @@ runs: #### - name: Check deployment rollout status - if: ${{ (hashFiles('k8s/deployment.yaml') != '' || hashFiles('k8s/deployment.yml') != '') && inputs.dry_run != 'true' }} + if: ${{ (hashFiles('k8s/deployment.yaml') != '' || hashFiles('k8s/deployment.yml') != '') && steps.if_production.outputs.dry_run != 'true' }} run: | if [ -f k8s/deployment.yaml ]; then kubectl rollout status deploy $(yq -r .metadata.name k8s/deployment.yaml) From 92715402f5f84d5f9f56f1638fa2a5f58ab91634 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Fri, 20 Sep 2024 16:08:36 -0500 Subject: [PATCH 07/24] update morgan to skip logging ping --- src/utils/morgan.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/morgan.js b/src/utils/morgan.js index 9ff999d..72d1769 100644 --- a/src/utils/morgan.js +++ b/src/utils/morgan.js @@ -5,7 +5,9 @@ const morgan = require('morgan') // This method is not really needed here since // we already told to the logger that it should // print only error messages in test. -const skip = () => { +const skip = (req) => { + if (req.url === '/ping') return true + const env = process.env.NODE_ENV || 'development' return env === 'test' } From 0486a46758737fae85b9247d5daf64e3a406a30e Mon Sep 17 00:00:00 2001 From: Jimmy Date: Fri, 20 Sep 2024 16:29:14 -0500 Subject: [PATCH 08/24] Update winston.js to only format and colorize logs locally --- src/utils/winston.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/utils/winston.js b/src/utils/winston.js index 4416c67..c076cac 100644 --- a/src/utils/winston.js +++ b/src/utils/winston.js @@ -25,6 +25,7 @@ const level = () => { case 'test': return 'error' + case 'local': case 'development': return 'debug' @@ -58,14 +59,7 @@ const format = () => { // Add the message timestamp with the preferred format winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), // Define the JSON format for pretty print - (env === 'development' ? winston.format.json({ replacer: null, space: 2 }) : winston.format.json()) - // winston.format.prettyPrint(), - /* - * Define the format of the message showing the timestamp, the level and the message - winston.format.printf( - (info) => `${info.timestamp} ${info.level}: ${info.message}` - ) - */ + (['local'].includes(env) ? winston.format.json({ replacer: null, space: 2 }) : winston.format.json() ) } @@ -79,14 +73,19 @@ const defaultTransports = () => { new winston.transports.Console({ format: winston.format.combine( // Tell Winston that the logs must be colored - winston.format.colorize({ all: env === 'development' }) + winston.format.colorize({ all: env === 'local' }) ) }), - // Allow error level messages to print to the error.log file - new winston.transports.File({ - filename: 'logs/error.log', - level: 'error' - }) + ...(['local'].includes(env) + ? [ + new winston.transports.File({ + // Allow error level messages to print to the error.log file + filename: 'logs/error.log', + level: 'error' + }) + ] + : [] + ) ] } @@ -105,6 +104,6 @@ module.exports = (transports = defaultTransports()) => { transports, exceptionHandlers: transports, rejectionHandlers: transports, - exitOnError: env !== 'development' + exitOnError: !(['local', 'development'].includes(env)) }) } From fffe71188d78f9e7c0ff772d5eb395e73d9c1519 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 22 Oct 2024 08:58:51 -0500 Subject: [PATCH 09/24] Update request-id.js to include correlation Id --- src/utils/request-id.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/utils/request-id.js b/src/utils/request-id.js index 456a4d1..880a5e3 100644 --- a/src/utils/request-id.js +++ b/src/utils/request-id.js @@ -1,9 +1,17 @@ +const crypto = require('crypto'); const { v4 } = require('uuid') function generateV4UUID (_request) { return v4() } +function generateHash(_request) { + const hash = crypto.createHash('sha1'); + hash.update(v4(_request)); + return hash.digest('hex'); +} + +const CORRELATION_ATTRIBUTE_NAME = 'correlationId' const TRACE_ATTRIBUTE_NAME = 'traceId' const SPAN_ATTRIBUTE_NAME = 'spanId' @@ -20,6 +28,11 @@ const SPAN_ATTRIBUTE_NAME = 'spanId' * @returns {function(ExpressRequest, ExpressResponse, ExpressNextFunction): void} - The middleware function to be passed to Express */ module.exports = function requestId ({ + correlation = { + generator: generateHash, + headerName: 'X-Correlation-Id', + setHeader: true + }, trace = { generator: generateV4UUID, headerName: 'X-Request-Id', @@ -31,6 +44,19 @@ module.exports = function requestId ({ } } = {}) { return function (request, response, next) { + /* + * Correlation Id + */ + const incomingCorrelationId = request.get(correlation.headerName) + const correlationId = incomingCorrelationId === undefined ? correlation.generator(request) : incomingCorrelationId + + if (correlation.setHeader) { + response.set(correlation.headerName, correlationId) + } + + request[CORRELATION_ATTRIBUTE_NAME] = correlationId + /* END Correlation Id */ + /* * Trace Id */ From 21d153310beba794e5573bf370c3976208e7ba0a Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 22 Oct 2024 09:01:01 -0500 Subject: [PATCH 10/24] Update morgan.js to include correlation Id --- src/utils/morgan.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/morgan.js b/src/utils/morgan.js index 72d1769..18def0d 100644 --- a/src/utils/morgan.js +++ b/src/utils/morgan.js @@ -12,6 +12,10 @@ const skip = (req) => { return env === 'test' } +morgan.token('correlationId', function (req, res) { + return req.correlationId +}) + morgan.token('traceId', function (req, res) { return req.traceId }) @@ -33,6 +37,7 @@ module.exports = (logger) => { ? '[:traceId][:spanId] :method :url' : function (tokens, req, res) { return JSON.stringify({ + correlationId: tokens.correlationId(req, res), traceId: tokens.traceId(req, res), spanId: tokens.spanId(req, res), method: tokens.method(req, res), @@ -65,6 +70,7 @@ module.exports = (logger) => { ? '[:traceId][:spanId] :status :res[content-length] - :response-time ms' : function (tokens, req, res) { return JSON.stringify({ + correlationId: tokens.correlationId(req, res), traceId: tokens.traceId(req, res), spanId: tokens.spanId(req, res), status: Number.parseFloat(tokens.status(req, res) || '0.0'), From 571efaad7aadebe8c8c34e4c2e332f62ec1d1806 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 22 Oct 2024 09:04:03 -0500 Subject: [PATCH 11/24] Update loggerMiddleware.js to add correlation Id --- src/utils/loggerMiddleware.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/loggerMiddleware.js b/src/utils/loggerMiddleware.js index 06785b9..6bb7825 100644 --- a/src/utils/loggerMiddleware.js +++ b/src/utils/loggerMiddleware.js @@ -1,6 +1,7 @@ module.exports = (logger) => { return function (request, response, next) { const reqId = { + correlationId: request.correlationId, traceId: request.traceId, spanId: request.spanId } From bd5b71ed55120af546e114fecff92d86cc8c3658 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 22 Oct 2024 10:08:18 -0500 Subject: [PATCH 12/24] Update axios.js to send correlation Id --- src/utils/axios.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/axios.js b/src/utils/axios.js index eed2b49..d36745e 100644 --- a/src/utils/axios.js +++ b/src/utils/axios.js @@ -19,6 +19,7 @@ function generateSUID (_request) { * @returns {function(ExpressRequest): AxiosInstance} - A function to use in the Express route handlers to create any needed Axios instances */ module.exports = function svcAgent ({ + correlationIdHeader = 'X-Correlation-Id', traceIdHeader = 'X-Request-Id', spanIdHeader = 'X-svc2svc-Id', generator = generateSUID, @@ -30,6 +31,7 @@ module.exports = function svcAgent ({ const error = process.env.NODE_ENV === 'test' ? () => {} : expressRequest.logger?.error || console.error const defaultHeaders = new axios.AxiosHeaders(axiosConfig.headers) + defaultHeaders.set(correlationIdHeader, expressRequest.correlationId) defaultHeaders.set(traceIdHeader, expressRequest.traceId) const client = axios.create({ From c37d8c56f2e1833f1892b202893411b3daf7f846 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Fri, 3 Jan 2025 21:17:14 -0600 Subject: [PATCH 13/24] update workflows --- .dockerignore | 3 ++- .github/workflows/build.yml | 6 ++--- .github/workflows/build_and_push.yml | 33 +++++++++++++++++++------ .github/workflows/test.yml | 25 ++++++++++--------- bin/github/workflows/build.yml | 6 ++--- bin/github/workflows/build_and_push.yml | 33 +++++++++++++++++++------ bin/github/workflows/test.yml | 25 ++++++++++--------- 7 files changed, 86 insertions(+), 45 deletions(-) diff --git a/.dockerignore b/.dockerignore index 308b330..8565c25 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,5 +17,6 @@ npm-debug.log .dockerignore .eslint* .gitignore +compose.y*ml Dockerfile -README.md \ No newline at end of file +README.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 848148a..b1e5105 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,12 +13,10 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/docker@master with: debug: ${{ vars.pipeline_debug }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build_and_push.yml b/.github/workflows/build_and_push.yml index 76031ff..625c4c6 100644 --- a/.github/workflows/build_and_push.yml +++ b/.github/workflows/build_and_push.yml @@ -6,24 +6,43 @@ on: - main - master paths: - - 'package.json' - - 'src/**' - - 'Dockerfile' - - '.dockerignore' + - "package.json" + - "src/**" + - "Dockerfile" + - ".dockerignore" jobs: - version_and_build_and_push: + build_and_push_to_docker: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: jabez007/create-express-template/.github/shared/version@master + + - uses: jabez007/docker-kitchen/.github/shared/docker@master + with: + push: true + image-name: ${{ vars.DOCKER_USERNAME }}/${{ github.event.repository.name }} + debug: ${{ vars.pipeline_debug }} + env: + DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_TOKEN }} + + build_and_push_to_ghcr: runs-on: ubuntu-latest permissions: contents: read packages: write # required if push is true steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/ghcr@master with: push: true debug: ${{ vars.pipeline_debug }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8183c74..bbb61a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,11 +12,14 @@ jobs: strategy: matrix: - node_version: [18.x, 20.x, 22.x] + node_version: + - 18.x + - 20.x + - 22.x steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-node-template/.github/shared/node/test@master with: @@ -24,14 +27,14 @@ jobs: test_docker: runs-on: ubuntu-latest - + steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/docker@master with: tag: test prune: false @@ -40,14 +43,14 @@ jobs: - name: Load image run: | docker load --input /tmp/${{ github.event.repository.name }}.tar - docker image ls -a - + docker image ls -a + - name: Run Docker container id: container_run run: | CONTAINER_ID=$(docker run -d -p 80:8888 ${{ github.repository }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - + - name: Test Docker container run: | sleep 10 @@ -57,13 +60,13 @@ jobs: echo "Ping endpoint test failed" exit 1 fi - + - name: Stop and remove Docker container if: always() run: | docker stop ${{ steps.container_run.outputs.CONTAINER_ID }} docker rm ${{ steps.container_run.outputs.CONTAINER_ID }} - + - name: Post cleanup if: always() - run: docker image prune -f \ No newline at end of file + run: docker image prune -f diff --git a/bin/github/workflows/build.yml b/bin/github/workflows/build.yml index 848148a..b1e5105 100644 --- a/bin/github/workflows/build.yml +++ b/bin/github/workflows/build.yml @@ -13,12 +13,10 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/docker@master with: debug: ${{ vars.pipeline_debug }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/bin/github/workflows/build_and_push.yml b/bin/github/workflows/build_and_push.yml index 76031ff..625c4c6 100644 --- a/bin/github/workflows/build_and_push.yml +++ b/bin/github/workflows/build_and_push.yml @@ -6,24 +6,43 @@ on: - main - master paths: - - 'package.json' - - 'src/**' - - 'Dockerfile' - - '.dockerignore' + - "package.json" + - "src/**" + - "Dockerfile" + - ".dockerignore" jobs: - version_and_build_and_push: + build_and_push_to_docker: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: jabez007/create-express-template/.github/shared/version@master + + - uses: jabez007/docker-kitchen/.github/shared/docker@master + with: + push: true + image-name: ${{ vars.DOCKER_USERNAME }}/${{ github.event.repository.name }} + debug: ${{ vars.pipeline_debug }} + env: + DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_TOKEN }} + + build_and_push_to_ghcr: runs-on: ubuntu-latest permissions: contents: read packages: write # required if push is true steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/ghcr@master with: push: true debug: ${{ vars.pipeline_debug }} diff --git a/bin/github/workflows/test.yml b/bin/github/workflows/test.yml index 8183c74..bbb61a9 100644 --- a/bin/github/workflows/test.yml +++ b/bin/github/workflows/test.yml @@ -12,11 +12,14 @@ jobs: strategy: matrix: - node_version: [18.x, 20.x, 22.x] + node_version: + - 18.x + - 20.x + - 22.x steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-node-template/.github/shared/node/test@master with: @@ -24,14 +27,14 @@ jobs: test_docker: runs-on: ubuntu-latest - + steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: jabez007/create-express-template/.github/shared/version@master - - uses: jabez007/aws-kubectl/.github/shared/docker@master + - uses: jabez007/docker-kitchen/.github/shared/docker@master with: tag: test prune: false @@ -40,14 +43,14 @@ jobs: - name: Load image run: | docker load --input /tmp/${{ github.event.repository.name }}.tar - docker image ls -a - + docker image ls -a + - name: Run Docker container id: container_run run: | CONTAINER_ID=$(docker run -d -p 80:8888 ${{ github.repository }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - + - name: Test Docker container run: | sleep 10 @@ -57,13 +60,13 @@ jobs: echo "Ping endpoint test failed" exit 1 fi - + - name: Stop and remove Docker container if: always() run: | docker stop ${{ steps.container_run.outputs.CONTAINER_ID }} docker rm ${{ steps.container_run.outputs.CONTAINER_ID }} - + - name: Post cleanup if: always() - run: docker image prune -f \ No newline at end of file + run: docker image prune -f From fc73905eeee4e5b095a687cc762b11e6ee817e03 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 06:53:24 -0600 Subject: [PATCH 14/24] implement Docker compose --- compose.yml | 20 ++++++++++++++++++++ package-lock.json | 3 +-- package.json | 7 +++---- 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 compose.yml diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..2c48729 --- /dev/null +++ b/compose.yml @@ -0,0 +1,20 @@ +services: + app: + #depends_on: + # db: + # condition: service_healthy + build: + context: . + ports: + - "8080:8080" + environment: + NODE_ENV: local + PORT: 8080 + #env_file: + # - ".env" + # - ".env.local" + #restart: on-failure + develop: + watch: + - path: ./src + action: rebuild diff --git a/package-lock.json b/package-lock.json index 8f4a1c0..6cf47c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "axios": "^1.7.3", + "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", "morgan": "^1.10.0", @@ -27,7 +28,6 @@ "create-express-template": "bin/index.js" }, "devDependencies": { - "dotenv": "^16.4.5", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.6.2", @@ -1332,7 +1332,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" diff --git a/package.json b/package.json index 707a194..f895edb 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,11 @@ "bin": "./bin/index.js", "main": "./src/index.js", "scripts": { - "prebuild:docker": "npm run lint", - "build:docker": "docker build --platform=linux/amd64 -t jabez07/create-express-template:$npm_package_version .", "prelint": "npm run version:short", "lint": "eslint --fix './src/**/*.{js,jsx,ts}'", "serve": "nodemon --watch src -e js,jsx --exec npm run start", - "serve:docker": "docker run --init --name create-express-template -p 80:8080 --env-file ./.env -d jabez07/create-express-template:$npm_package_version", + "serve:docker": "docker compose up --build --watch", + "postserve:docker": "docker compose down", "prestart": "npm run lint", "start": "node -r dotenv/config .", "start:docker": "node .", @@ -36,7 +35,6 @@ }, "homepage": "https://github.com/jabez007/create-express-template#readme", "devDependencies": { - "dotenv": "^16.4.5", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.6.2", @@ -50,6 +48,7 @@ "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "axios": "^1.7.3", + "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", "morgan": "^1.10.0", From db11252653ba7add3294470a30b699ee00c4c8d2 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 11:19:52 -0600 Subject: [PATCH 15/24] upgrade utils to packages --- package-lock.json | 248 +++++++++++++++--------------- package.json | 10 +- src/connections/services/swapi.js | 2 +- src/server.js | 38 +++-- src/utils/axios.js | 104 ------------- src/utils/loggerMiddleware.js | 31 ---- src/utils/morgan.js | 98 ------------ src/utils/request-id.js | 89 ----------- src/utils/winston.js | 109 ------------- test/logger.test.js | 83 ---------- test/request-id.test.js | 59 ------- test/svcAgent.test.js | 57 ------- 12 files changed, 148 insertions(+), 780 deletions(-) delete mode 100644 src/utils/axios.js delete mode 100644 src/utils/loggerMiddleware.js delete mode 100644 src/utils/morgan.js delete mode 100644 src/utils/request-id.js delete mode 100644 src/utils/winston.js delete mode 100644 test/logger.test.js delete mode 100644 test/request-id.test.js delete mode 100644 test/svcAgent.test.js diff --git a/package-lock.json b/package-lock.json index 6cf47c1..c217254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,20 +9,18 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@mccann-hub/express-log-smith": "^0.1.2", + "@mccann-hub/json-logger": "^0.1.2", + "@mccann-hub/service-agent": "^0.1.2", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", - "axios": "^1.7.3", "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", - "morgan": "^1.10.0", "path-to-regexp": "^7.1.0", - "short-unique-id": "^5.2.0", "swagger-jsdoc": "^6.2.8", "swagger-parser": "^10.0.3", - "swagger-ui-express": "^5.0.1", - "uuid": "^10.0.0", - "winston": "^3.13.1" + "swagger-ui-express": "^5.0.1" }, "bin": { "create-express-template": "bin/index.js" @@ -86,7 +84,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -237,6 +234,33 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "license": "MIT" }, + "node_modules/@mccann-hub/express-log-smith": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@mccann-hub/express-log-smith/-/express-log-smith-0.1.2.tgz", + "integrity": "sha512-vmb7QwD1uf52q8PL925pdF475fQiEhMH7IuVIHQtUVbbfaDuBZjCeXY4c8+CaTu3pArkoKOwFm9JR7pXbQBpww==", + "dependencies": { + "express": "^4.21.1", + "morgan": "^1.10.0", + "uuid": "^11.0.3" + } + }, + "node_modules/@mccann-hub/json-logger": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@mccann-hub/json-logger/-/json-logger-0.1.2.tgz", + "integrity": "sha512-mWe3TBS7DFMgqN4PTBPl9GIz+vwo/+RAio020LvP3wtUYffKnV/xSTdDDZg/RUfTP+3pgTBLGUx2LCQb2dK3xQ==", + "dependencies": { + "winston": "^3.16.0" + } + }, + "node_modules/@mccann-hub/service-agent": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@mccann-hub/service-agent/-/service-agent-0.1.2.tgz", + "integrity": "sha512-ajdlem7o+/II32Mri1IIXRAhCftf/dwiexkQndm0adJD6yH9cDdQxojekwC+kZxAJfjWj6WO5DB5fhIPC/EJzw==", + "dependencies": { + "axios": "^1.7.7", + "short-unique-id": "^5.2.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -402,8 +426,7 @@ "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", @@ -702,10 +725,9 @@ } }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "license": "MIT", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -750,10 +772,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "license": "MIT", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -763,7 +784,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -777,7 +798,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -785,8 +805,7 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -858,7 +877,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1096,16 +1114,14 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -1289,7 +1305,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -1360,10 +1375,9 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -1519,8 +1533,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -1964,43 +1977,41 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "license": "MIT", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -2009,6 +2020,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -2027,10 +2042,9 @@ "license": "MIT" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "license": "MIT" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -2081,8 +2095,7 @@ "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -2112,13 +2125,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "license": "MIT", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -2133,7 +2145,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -2141,8 +2152,7 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-up": { "version": "5.0.0", @@ -2595,7 +2605,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -2611,7 +2620,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -3215,10 +3223,9 @@ } }, "node_modules/logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "license": "MIT", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -3241,10 +3248,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", @@ -3691,7 +3700,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -3925,12 +3933,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "license": "BSD-3-Clause", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -3984,7 +3991,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3999,7 +4005,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4212,10 +4217,9 @@ } }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "license": "MIT", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "engines": { "node": ">=10" } @@ -4223,8 +4227,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { "version": "6.3.1", @@ -4237,10 +4240,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "license": "MIT", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -4264,7 +4266,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -4272,14 +4273,20 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serialize-javascript": { "version": "6.0.2", @@ -4292,15 +4299,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "license": "MIT", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -4342,8 +4348,7 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -4446,7 +4451,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4455,7 +4459,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } @@ -4742,7 +4745,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", "engines": { "node": ">=0.6" } @@ -4761,7 +4763,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", "engines": { "node": ">= 14.0.0" } @@ -4930,7 +4931,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4949,8 +4949,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -4962,16 +4961,15 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/validator": { @@ -5047,34 +5045,32 @@ } }, "node_modules/winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", - "license": "MIT", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.6.0", + "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" + "winston-transport": "^4.9.0" }, "engines": { "node": ">= 12.0.0" } }, "node_modules/winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", - "license": "MIT", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "dependencies": { - "logform": "^2.6.1", + "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, diff --git a/package.json b/package.json index f895edb..67bd4ca 100644 --- a/package.json +++ b/package.json @@ -45,20 +45,18 @@ "supertest": "^7.0.0" }, "dependencies": { + "@mccann-hub/express-log-smith": "^0.1.2", + "@mccann-hub/json-logger": "^0.1.2", + "@mccann-hub/service-agent": "^0.1.2", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", - "axios": "^1.7.3", "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", - "morgan": "^1.10.0", "path-to-regexp": "^7.1.0", - "short-unique-id": "^5.2.0", "swagger-jsdoc": "^6.2.8", "swagger-parser": "^10.0.3", - "swagger-ui-express": "^5.0.1", - "uuid": "^10.0.0", - "winston": "^3.13.1" + "swagger-ui-express": "^5.0.1" }, "_moduleAliases": { "@": "./src", diff --git a/src/connections/services/swapi.js b/src/connections/services/swapi.js index 6d26de6..ccafb72 100644 --- a/src/connections/services/swapi.js +++ b/src/connections/services/swapi.js @@ -1,4 +1,4 @@ -const svcAgent = require('@utils/axios') +const svcAgent = require('@mccann-hub/service-agent').default const swapiClient = svcAgent({ axiosConfig: { diff --git a/src/server.js b/src/server.js index 144823b..24ee32a 100644 --- a/src/server.js +++ b/src/server.js @@ -3,13 +3,13 @@ * i.e. Middleware and Routes */ const express = require('express') -const Logger = require('@utils/winston') -const requestId = require('@utils/request-id') const swaggerUi = require('swagger-ui-express') +const Logger = require('@mccann-hub/json-logger').default const swaggerValidator = require('@utils/validator') const openapiSpecification = require('@utils/swagger') -const loggerMiddleware = require('@utils/loggerMiddleware') -const { requestMorgan, responseMorgan } = require('@utils/morgan')(Logger()) +const { dexter, requestId, requestLogger } = require('@mccann-hub/express-log-smith') + +const { requestMorgan, responseMorgan } = dexter(Logger()) const app = express() @@ -21,7 +21,9 @@ app.use(requestId()) app.use(requestMorgan) app.use(responseMorgan) -app.use(loggerMiddleware(Logger())) +// body parser + +app.use(requestLogger(Logger())) app.use(swaggerValidator(openapiSpecification)) /* END Middlewares */ @@ -41,20 +43,22 @@ app.use('/swagger.json', (req, res) => { res.json(openapiSpecification) }) -const swaggerOptions = { - swaggerOptions: { - url: '/swagger.json', - validatorUrl: 'localhost' +if (['local', 'dev', 'develop', 'development'].includes(process.env.NODE_ENV || 'development')) { + const swaggerOptions = { + swaggerOptions: { + url: '/swagger.json', + validatorUrl: 'localhost' + } } -} -app.use('/docs', - swaggerUi.serveFiles(null, swaggerOptions), - swaggerUi.setup(null, { - ...swaggerOptions, - explorer: false - }) -) + app.use('/docs', + swaggerUi.serveFiles(undefined, swaggerOptions), + swaggerUi.setup(undefined, { + ...swaggerOptions, + explorer: false + }) + ) +} /* END Swagger */ module.exports = app diff --git a/src/utils/axios.js b/src/utils/axios.js deleted file mode 100644 index d36745e..0000000 --- a/src/utils/axios.js +++ /dev/null @@ -1,104 +0,0 @@ -const axios = require('axios') -const ShortUniqueId = require('short-unique-id') - -const suid = new ShortUniqueId({ - dictionary: 'hex' -}) - -function generateSUID (_request) { - return suid.formattedUUID('$r4-$r2-$r2-$r2-$r6') -} - -/** - * Gives a function for creating Axios instances specific to an Express request to facilitate tracing across interconnected microservices - * @param {Object} param0 - * @param {string} param0.traceIdHeader - The header to include in all outgoing requests to connect all the microservices calls that were trigger by a single inbound request - * @param {string} param0.spanIdHeader - The header to include in all outgoing requests to track the individual calls one microservice makes to another - * @param {function(ExpressRequest): string} param0.generator - A function to generate the unique Id for the spanIdHeader - * @param {AxiosConfig} param0.axiosConfig - An Axios config object to pass to axios.create - * @returns {function(ExpressRequest): AxiosInstance} - A function to use in the Express route handlers to create any needed Axios instances - */ -module.exports = function svcAgent ({ - correlationIdHeader = 'X-Correlation-Id', - traceIdHeader = 'X-Request-Id', - spanIdHeader = 'X-svc2svc-Id', - generator = generateSUID, - axiosConfig = {} -} = {}) { - return function (expressRequest) { - const debug = process.env.NODE_ENV === 'test' ? () => {} : expressRequest.logger?.debug || console.log - const info = process.env.NODE_ENV === 'test' ? () => {} : expressRequest.logger?.info || console.log - const error = process.env.NODE_ENV === 'test' ? () => {} : expressRequest.logger?.error || console.error - - const defaultHeaders = new axios.AxiosHeaders(axiosConfig.headers) - defaultHeaders.set(correlationIdHeader, expressRequest.correlationId) - defaultHeaders.set(traceIdHeader, expressRequest.traceId) - - const client = axios.create({ - ...axiosConfig, - ...{ - headers: defaultHeaders - } - }) - - client.interceptors.request.use( - (req) => { - const spanId = generator(expressRequest) - info(`sending request ${spanId}`, { childSpanId: spanId }) - req.headers.set(spanIdHeader, spanId) - debug('sending request', { axios: req }) - return req - }, - (err) => { - let axios = { - request: err.config - } - if (err.request) { - axios = { - ...axios - // TODO: add information from the http.ClientRequest object? - } - } - error(err.message, { axios }) - return Promise.reject(err) - } - ) - - client.interceptors.response.use( - (res) => { - debug('received response', { - axios: { - request: res.config, - status: res.status, - statusText: res.statusText, - headers: res.headers, - data: res.data - } - }) - const spanId = res.headers[spanIdHeader] || res.config.headers[spanIdHeader] - if (spanId) { - info(`received response ${spanId}`, { childSpanId: spanId }) - } - return res - }, - (err) => { - let axios = { - request: err.config - } - if (err.response) { - axios = { - ...axios, - status: err.response.status, - statusText: err.response.statusText, - headers: err.response.headers, - data: err.response.data - } - } - error(err.message, { axios }) - return Promise.reject(err) - } - ) - - return client - } -} diff --git a/src/utils/loggerMiddleware.js b/src/utils/loggerMiddleware.js deleted file mode 100644 index 6bb7825..0000000 --- a/src/utils/loggerMiddleware.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = (logger) => { - return function (request, response, next) { - const reqId = { - correlationId: request.correlationId, - traceId: request.traceId, - spanId: request.spanId - } - - // .child is likely to be bugged - request.logger = { - debug: (message, metadata = {}) => logger.debug(message, { - ...metadata, - ...reqId - }), - info: (message, metadata = {}) => logger.info(message, { - ...metadata, - ...reqId - }), - warn: (message, metadata = {}) => logger.warn(message, { - ...metadata, - ...reqId - }), - error: (message, metadata = {}) => logger.error(message, { - ...metadata, - ...reqId - }) - } - - next() - } -} diff --git a/src/utils/morgan.js b/src/utils/morgan.js deleted file mode 100644 index 18def0d..0000000 --- a/src/utils/morgan.js +++ /dev/null @@ -1,98 +0,0 @@ -const morgan = require('morgan') - -// Skip all the Morgan http log if the -// application is running in test mode. -// This method is not really needed here since -// we already told to the logger that it should -// print only error messages in test. -const skip = (req) => { - if (req.url === '/ping') return true - - const env = process.env.NODE_ENV || 'development' - return env === 'test' -} - -morgan.token('correlationId', function (req, res) { - return req.correlationId -}) - -morgan.token('traceId', function (req, res) { - return req.traceId -}) - -morgan.token('spanId', function (req, res) { - return req.spanId -}) - -module.exports = (logger) => { - const log = logger?.http || console.log - - return { - requestMorgan: morgan( - // Define message format string. - // The message format is made from tokens, and each token is - // defined inside the Morgan library. - // You can create your custom token to show what do you want from a request. - (!logger?.http - ? '[:traceId][:spanId] :method :url' - : function (tokens, req, res) { - return JSON.stringify({ - correlationId: tokens.correlationId(req, res), - traceId: tokens.traceId(req, res), - spanId: tokens.spanId(req, res), - method: tokens.method(req, res), - url: tokens.url(req, res) - }) - } - ), - // Options: - { - // Override the stream method by telling - // Morgan to use our custom logger instead of the console.log. - stream: { - // Use the http severity - write: (message) => ( - logger?.http - ? log('incoming request', JSON.parse(message)) - : log(message) // this has a "bug" where it writes an extra newline - ) - }, - immediate: true, - skip - } - ), - responseMorgan: morgan( - // Define message format string (this is the default one). - // The message format is made from tokens, and each token is - // defined inside the Morgan library. - // You can create your custom token to show what do you want from a request. - (!logger?.http - ? '[:traceId][:spanId] :status :res[content-length] - :response-time ms' - : function (tokens, req, res) { - return JSON.stringify({ - correlationId: tokens.correlationId(req, res), - traceId: tokens.traceId(req, res), - spanId: tokens.spanId(req, res), - status: Number.parseFloat(tokens.status(req, res) || '0.0'), - contentLength: tokens.res(req, res, 'content-length') || '-', - responseTime: Number.parseFloat(tokens['response-time'](req, res) || '0.0') - }) - } - ), - // Options: - { - // Override the stream method by telling - // Morgan to use our custom logger instead of the console.log. - stream: { - // Use the http severity - write: (message) => ( - logger?.http - ? log('outgoing response', JSON.parse(message)) - : log(message) // this has a "bug" where it writes an extra newline - ) - }, - skip - } - ) - } -} diff --git a/src/utils/request-id.js b/src/utils/request-id.js deleted file mode 100644 index 880a5e3..0000000 --- a/src/utils/request-id.js +++ /dev/null @@ -1,89 +0,0 @@ -const crypto = require('crypto'); -const { v4 } = require('uuid') - -function generateV4UUID (_request) { - return v4() -} - -function generateHash(_request) { - const hash = crypto.createHash('sha1'); - hash.update(v4(_request)); - return hash.digest('hex'); -} - -const CORRELATION_ATTRIBUTE_NAME = 'correlationId' -const TRACE_ATTRIBUTE_NAME = 'traceId' -const SPAN_ATTRIBUTE_NAME = 'spanId' - -/** - * Creates an Express middleware function to inject an "id" in to each incoming request - * @param {Object} param0 - * @param {Object} param0.trace - An object for configuring how traceId is passed around. - * @param {function(ExpressRequest): string} param0.trace.generator - A function to generate the Trace Id if missing from an incoming request - * @param {string} param0.trace.headerName - Which header to look for in the incoming request for the Trace Id - * @param {boolean} param0.trace.setHeader - Should the outgoing response include the header for the Trace Id? - * @param {Object} param0.span - An object for configuring how spanId is passed around. - * @param {string} param0.span.headerName - Which header to look for in the incoming request for the Span Id - * @param {boolean} param0.span.setHeader - Should the outgoing response include the header for the Span Id? - * @returns {function(ExpressRequest, ExpressResponse, ExpressNextFunction): void} - The middleware function to be passed to Express - */ -module.exports = function requestId ({ - correlation = { - generator: generateHash, - headerName: 'X-Correlation-Id', - setHeader: true - }, - trace = { - generator: generateV4UUID, - headerName: 'X-Request-Id', - setHeader: true - }, - span = { - headerName: 'X-svc2svc-Id', - setHeader: true - } -} = {}) { - return function (request, response, next) { - /* - * Correlation Id - */ - const incomingCorrelationId = request.get(correlation.headerName) - const correlationId = incomingCorrelationId === undefined ? correlation.generator(request) : incomingCorrelationId - - if (correlation.setHeader) { - response.set(correlation.headerName, correlationId) - } - - request[CORRELATION_ATTRIBUTE_NAME] = correlationId - /* END Correlation Id */ - - /* - * Trace Id - */ - const incomingTraceId = request.get(trace.headerName) - const traceId = incomingTraceId === undefined ? trace.generator(request) : incomingTraceId - - if (trace.setHeader) { - response.set(trace.headerName, traceId) - } - - request[TRACE_ATTRIBUTE_NAME] = traceId - /* END Trace Id */ - - /* - * Span Id - */ - const incomingSpanId = request.get(span.headerName) - - if (incomingSpanId) { - request[SPAN_ATTRIBUTE_NAME] = incomingSpanId - - if (span.setHeader) { - response.set(span.headerName, incomingSpanId) - } - } - /* END Span Id */ - - next() - } -} diff --git a/src/utils/winston.js b/src/utils/winston.js deleted file mode 100644 index c076cac..0000000 --- a/src/utils/winston.js +++ /dev/null @@ -1,109 +0,0 @@ -const winston = require('winston') - -// Define your severity levels. -// With them, You can create log files, -// see or hide levels based on the running ENV. -const levels = { - error: 0, - warn: 1, - http: 2, - info: 3, - debug: 4 -} - -// This method set the current severity based on -// the current NODE_ENV: show all the log levels -// if the server was run in development mode; otherwise, -// if it was run in production, show only info, http, warn, and error messages. -const level = () => { - if (process.env.WINSTON_LEVEL) { - return process.env.WINSTON_LEVEL - } - - const env = process.env.NODE_ENV || 'development' - switch (env) { - case 'test': - return 'error' - - case 'local': - case 'development': - return 'debug' - - default: - return 'info' - } -} - -// Define different colors for each level. -// Colors make the log message more visible, -// adding the ability to focus or ignore messages. -const colors = { - error: 'red', - warn: 'yellow', - http: 'magenta', - info: 'green', - debug: 'white' -} - -// Tell winston that you want to link the colors -// defined above to the severity levels. -winston.addColors(colors) - -// Chose the aspect of your log customizing the log format. -const format = () => { - const env = process.env.NODE_ENV || 'development' - - return winston.format.combine( - // Make sure to write the stack when logging an error - winston.format.errors({ stack: true }), - // Add the message timestamp with the preferred format - winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), - // Define the JSON format for pretty print - (['local'].includes(env) ? winston.format.json({ replacer: null, space: 2 }) : winston.format.json() - ) -} - -// Define which transports the logger must use to print out messages. -// In this example, we are using two different transports -const defaultTransports = () => { - const env = process.env.NODE_ENV || 'development' - - return [ - // Allow the use of the console to print all messages - new winston.transports.Console({ - format: winston.format.combine( - // Tell Winston that the logs must be colored - winston.format.colorize({ all: env === 'local' }) - ) - }), - ...(['local'].includes(env) - ? [ - new winston.transports.File({ - // Allow error level messages to print to the error.log file - filename: 'logs/error.log', - level: 'error' - }) - ] - : [] - ) - ] -} - -module.exports = (transports = defaultTransports()) => { - const env = process.env.NODE_ENV || 'development' - - // Create the logger instance that has to be exported - // and used to log messages. - return winston.createLogger({ - level: level(), - defaultMeta: { - service: process.env.npm_package_name - }, - levels, - format: format(), - transports, - exceptionHandlers: transports, - rejectionHandlers: transports, - exitOnError: !(['local', 'development'].includes(env)) - }) -} diff --git a/test/logger.test.js b/test/logger.test.js deleted file mode 100644 index 69fb968..0000000 --- a/test/logger.test.js +++ /dev/null @@ -1,83 +0,0 @@ -const assert = require('assert') -const winston = require('winston') -const { Writable } = require('stream') -const Logger = require('@utils/winston') -const loggerMiddleware = require('@utils/loggerMiddleware') -const { createRequest, createResponse } = require('node-mocks-http') - -describe('logger Constructor', () => { - let output - let stream - let transport - - beforeEach(() => { - output = '' - - stream = new Writable() - stream._write = (chunk, encoding, next) => { - output = output += chunk.toString() - next() - } - - transport = new winston.transports.Stream({ stream }) - }) - - it('writes logs in valid JSON', () => { - const logger = Logger(transport) - logger.error('test message') - - const logEvents = output.trim().split('\n') - assert(JSON.parse(logEvents[0]).message, 'test message') - }) - - it('includes the call stack for errors in the JSON log', () => { - const logger = Logger(transport) - logger.error(new Error('test message')) - - const logEvents = output.trim().split('\n') - const logObject = JSON.parse(logEvents[0]) - assert.equal(logObject.message, 'test message') - assert.notEqual(logObject.stack, undefined) - }) - - it('handles the traceId in the Express Request object', () => { - const mockRequest = createRequest() - mockRequest.traceId = 'foobar' - const mockResponse = createResponse() - const nextFunction = () => {} - - const logger = Logger(transport) - - loggerMiddleware(logger)(mockRequest, mockResponse, nextFunction) - - mockRequest.logger.error(new Error('test message')) - - const logEvents = output.trim().split('\n') - const logObject = JSON.parse(logEvents[0]) - assert.equal(logObject.message, 'test message') - assert.notEqual(logObject.stack, undefined) - assert.equal(logObject.traceId, 'foobar') - assert.equal(logObject.spanId, undefined) - }) - - it('handles the spanId in the Express Request object', () => { - const mockRequest = createRequest() - mockRequest.traceId = 'foobar' - mockRequest.spanId = 'hello-world' - const mockResponse = createResponse() - const nextFunction = () => {} - - const logger = Logger(transport) - - loggerMiddleware(logger)(mockRequest, mockResponse, nextFunction) - - mockRequest.logger.error(new Error('test message')) - - const logEvents = output.trim().split('\n') - const logObject = JSON.parse(logEvents[0]) - assert.equal(logObject.message, 'test message') - assert.notEqual(logObject.stack, undefined) - assert.equal(logObject.traceId, 'foobar') - assert.equal(logObject.spanId, 'hello-world') - }) -}) diff --git a/test/request-id.test.js b/test/request-id.test.js deleted file mode 100644 index c59a6cf..0000000 --- a/test/request-id.test.js +++ /dev/null @@ -1,59 +0,0 @@ -const assert = require('assert') -const requestId = require('@utils/request-id') -const { createRequest, createResponse } = require('node-mocks-http') - -describe('requestId Middleware', () => { - let mockRequest - let mockResponse - const nextFunction = () => {} - - beforeEach(() => { - mockRequest = createRequest() - mockResponse = createResponse() - }) - - it('injects only traceId into Request object', () => { - requestId()(mockRequest, mockResponse, nextFunction) - assert.notEqual(mockRequest.traceId, undefined) - assert.equal(mockRequest.spanId, undefined) - }) - - it('uses the value from the existing traceId Request header', () => { - mockRequest = createRequest({ - headers: { - 'X-Request-Id': 'foobar' - } - }) - requestId()(mockRequest, mockResponse, nextFunction) - assert.equal(mockRequest.traceId, 'foobar') - assert.equal(mockRequest.spanId, undefined) - }) - - it('uses the value from the existing spanId Request header', () => { - mockRequest = createRequest({ - headers: { - 'X-svc2svc-Id': 'foobar' - } - }) - requestId()(mockRequest, mockResponse, nextFunction) - assert.notEqual(mockRequest.traceId, undefined) - assert.equal(mockRequest.spanId, 'foobar') - }) - - it('includes only the traceId in the Response headers', () => { - requestId()(mockRequest, mockResponse, nextFunction) - assert.notEqual(mockResponse.get('X-Request-Id'), undefined) - assert.equal(mockResponse.get('X-svc2svc-Id'), undefined) - }) - - it('includes the spanId in the Response headers', () => { - mockRequest = createRequest({ - headers: { - 'X-svc2svc-Id': 'foobar' - } - }) - requestId()(mockRequest, mockResponse, nextFunction) - assert.notEqual(mockResponse.get('X-Request-Id'), undefined) - assert.equal(mockResponse.get('X-svc2svc-Id'), 'foobar') - }) -}) diff --git a/test/svcAgent.test.js b/test/svcAgent.test.js deleted file mode 100644 index 418b59a..0000000 --- a/test/svcAgent.test.js +++ /dev/null @@ -1,57 +0,0 @@ -const axios = require('axios') -const assert = require('assert') -const svcAgent = require('@utils/axios') - -describe('svcAgent Constructor', () => { - let req - - beforeEach(() => { - req = { - traceId: 'foobar' - } - }) - - it('injects the X-Request-Id header outbound', () => { - const client = svcAgent()(req) - - assert.equal(client.defaults.headers['X-Request-Id'], 'foobar') - }) - - it('does not overwrite passed in headers', () => { - const client = svcAgent( - { - axiosConfig: { - headers: { - 'X-Custom-Header': 'Hello World' - } - } - } - )(req) - - assert.equal(client.defaults.headers['X-Custom-Header'], 'Hello World') - assert.equal(client.defaults.headers['X-Request-Id'], 'foobar') - }) - - it('sets the baseURL of the returned instance', () => { - const client = svcAgent( - { - axiosConfig: { - baseURL: 'https://some-domain.com/api/' - } - } - )(req) - - assert.equal(client.defaults.baseURL, 'https://some-domain.com/api/') - assert.equal(client.defaults.headers['X-Request-Id'], 'foobar') - }) - - it('injects the X-svc2svc-Id header outbound', () => { - const client = svcAgent()(req) - const axiosRequest = client.interceptors.request.handlers[0].fulfilled({ - headers: new axios.AxiosHeaders(client.defaults.headers) - }) - - assert.notEqual(axiosRequest.headers['X-svc2svc-Id'], undefined) - assert.equal(client.defaults.headers['X-Request-Id'], 'foobar') - }) -}) From 8e9027b545262cac394a82d7647333bb45d5066c Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 11:27:22 -0600 Subject: [PATCH 16/24] create publish workflow --- .github/workflows/publish.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..4b92137 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,35 @@ +name: Use Shared Action to Publish + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + publish_to_npm: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Shared Publish to NPM + uses: McCann-Hub/create-typescript-template/.github/shared/npm/publish@master + with: + node_version: 22.x + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + publish_to_ghpr: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Shared Publish to GHPR + uses: McCann-Hub/create-typescript-template/.github/shared/ghpr/publish@master + with: + node_version: 22.x + env: + GHPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 8ee722dc59405580194e56bfd77573dd275d2bca Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 17:23:57 -0600 Subject: [PATCH 17/24] update install script --- .eslintrc | 2 +- bin/index.js | 98 ++++++++++++++-------------- package-lock.json | 158 ++-------------------------------------------- package.json | 3 +- 4 files changed, 58 insertions(+), 203 deletions(-) diff --git a/.eslintrc b/.eslintrc index e5f2ca6..b025af6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,4 +11,4 @@ } } ] -} \ No newline at end of file +} diff --git a/bin/index.js b/bin/index.js index 552ef2a..6b1645c 100644 --- a/bin/index.js +++ b/bin/index.js @@ -23,7 +23,7 @@ if (process.argv.slice(2).length > 0) { const projectWorkingDirectory = join(initWorkingDirectory, folderName) /* #### END #### */ -async function main () { +async function main() { /* * make new directory and move into it */ @@ -57,6 +57,19 @@ async function main () { await exec('npm pkg set scripts.start="node -r dotenv/config ."') /* #### END #### */ + /* + * install dotENV + */ + console.log('installing dotENV (this may take a while)') + await exec('npm install dotenv') + + console.log('writing .env file') + await writeFile(join(projectWorkingDirectory, '.env'), 'PORT=8080') + + console.log('copying development .env file') + await cp(join(__dirname, '..', '.env.development'), join(projectWorkingDirectory, '.env.development')) + /* #### END #### */ + /* * install module-alias */ @@ -80,33 +93,19 @@ async function main () { console.log('adding alias for databases') await exec('npm pkg set _moduleAliases.@databases=./src/connections/databases') - /* #### END #### */ /* - * install dotENV + * install scope packages */ - console.log('installing dotENV (this may take a while)') - await exec('npm install --save-dev dotenv') + console.log('installing json-logger (this may take a while)') + await exec('npm install @mccann-hub/json-logger') - console.log('writing .env file') - await writeFile(join(projectWorkingDirectory, '.env'), 'PORT=8080') + console.log('installing service-agent (this may take a while)') + await exec('npm install @mccann-hub/service-agent') - console.log('copying development .env file') - await cp(join(__dirname, '..', '.env.development'), join(projectWorkingDirectory, '.env.development')) - /* #### END #### */ - - /* - * install Winston - */ - console.log('installing Winston (this may take a while)') - await exec('npm install winston') - - console.log('installing uuid (this may take a while)') - await exec('npm install uuid') - - console.log('installing Short Unique Id (this may take a while)') - await exec('npm install short-unique-id') + console.log('installing express-log-smith (this may take a while)') + await exec('npm install @mccann-hub/express-log-smith') /* #### END #### */ /* @@ -117,8 +116,21 @@ async function main () { console.log('installing Standard ESLint config (this may take a while)') await exec('npm install --save-dev eslint-config-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-n') - console.log('copying eslintrc file') - await cp(join(__dirname, '..', '.eslintrc'), join(projectWorkingDirectory, '.eslintrc')) + console.log('writing eslintrc file') + await writeFile(join(projectWorkingDirectory, '.eslintrc'), JSON.stringify({ + extends: [ + 'eslint:recommended', + 'standard' + ], + overrides: [ + { + files: './test/**/*.{test,spec}.js', + rules: { + 'no-undef': 'off' + } + } + ] + }, null, 2)) console.log('writing eslintignore file') await writeFile(join(projectWorkingDirectory, '.eslintignore'), 'node_modules') @@ -130,21 +142,11 @@ async function main () { await exec('npm pkg set scripts.prestart="npm run lint"') /* #### END #### */ - /* - * install Axios - */ - console.log('installing Axios (this may take a while)') - await exec('npm install axios') - /* #### END #### */ - /* * install Express */ console.log('installing ExpressJS (this may take a while)') await exec('npm install express') - - console.log('installing Morgan (this may take a while)') - await exec('npm install morgan') /* #### END #### */ /* @@ -163,20 +165,24 @@ async function main () { console.log('installing Swagger JSdoc (this may take a while)') await exec('npm install swagger-jsdoc') + console.log('installing Swagger UI Express (this may take a while)') + await exec('npm install swagger-ui-express') + /* #### END #### */ + + /* + * Packages for validator + */ console.log('installing Swagger Parser (this may take a while)') await exec('npm install swagger-parser') console.log('installing Path to Regexp (this may take a while)') - await exec('npm install path-to-regexp') + await exec('npm install path-to-regexp@7.2.0') console.log('installing Another JSON Validator (this may take a while)') await exec('npm install ajv') console.log('installing AJV Formats (this may take a while)') await exec('npm install ajv-formats') - - console.log('installing Swagger UI Express (this may take a while)') - await exec('npm install swagger-ui-express') /* #### END #### */ /* @@ -185,9 +191,6 @@ async function main () { console.log('installing Mocha (this may take a while)') await exec('npm install --save-dev mocha') - console.log('installing node-mocks-http (this may take a while)') - await exec('npm install --save-dev node-mocks-http') - console.log('installing SuperTest (this may take a while)') await exec('npm install --save-dev supertest') @@ -212,20 +215,23 @@ async function main () { console.log('copying dockerignore') await cp(join(__dirname, '..', '.dockerignore'), join(projectWorkingDirectory, '.dockerignore')) - console.log('copying Dockerfile') - await cp(join(__dirname, '..', 'Dockerfile'), join(projectWorkingDirectory, 'Dockerfile')) - console.log('adding start:docker to scripts in package.json') await exec('npm pkg set scripts.start:docker="node ."') + console.log('copying Dockerfile') + await cp(join(__dirname, '..', 'Dockerfile'), join(projectWorkingDirectory, 'Dockerfile')) + console.log('adding build:docker to scripts in package.json') - await exec(`npm pkg set scripts.build:docker="docker build --platform=linux/amd64 -t ${dockerUser}/${folderName}:$npm_package_version ."`) + await exec(`npm pkg set scripts.build:docker="docker build -t ${dockerUser}/${folderName}:$npm_package_version ."`) console.log('adding prebuild:docker to scripts in package.json') await exec('npm pkg set scripts.prebuild:docker="npm run lint"') + console.log('copying Docker compose') + await cp(join(__dirname, '..', 'compose.yml'), join(projectWorkingDirectory, 'compose.yml')) + console.log('adding serve:docker to scripts in package.json') - await exec(`npm pkg set scripts.serve:docker="docker run --init --name ${folderName} -p 80:8080 --env-file ./.env -d ${dockerUser}/${folderName}:$npm_package_version"`) + await exec('npm pkg set scripts.serve:docker="docker compose up --build --watch"') /* * Kubernetes diff --git a/package-lock.json b/package-lock.json index c217254..12a38d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", - "path-to-regexp": "^7.1.0", + "path-to-regexp": "^7.2.0", "swagger-jsdoc": "^6.2.8", "swagger-parser": "^10.0.3", "swagger-ui-express": "^5.0.1" @@ -31,7 +31,6 @@ "eslint-plugin-n": "^16.6.2", "eslint-plugin-promise": "^6.4.0", "mocha": "^10.6.0", - "node-mocks-http": "^1.15.0", "nodemon": "^3.1.4", "supertest": "^7.0.0" } @@ -302,60 +301,6 @@ "node": ">= 8" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -369,60 +314,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", @@ -3494,40 +3385,6 @@ "node": ">= 0.6" } }, - "node_modules/node-mocks-http": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.15.0.tgz", - "integrity": "sha512-3orGBAxXrnwz3ixU8AZpa0x8srAvVSHvbWanAqd5F0zVCVA2QstxaVcTSarFcjz4+pFSnR1zm28MsV83s/BtmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.10.6", - "accepts": "^1.3.7", - "content-disposition": "^0.5.3", - "depd": "^1.1.0", - "fresh": "^0.5.2", - "merge-descriptors": "^1.0.1", - "methods": "^1.1.2", - "mime": "^1.3.4", - "parseurl": "^1.3.3", - "range-parser": "^1.2.0", - "type-is": "^1.6.18" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/node-mocks-http/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/nodemon": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", @@ -3853,9 +3710,9 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-7.1.0.tgz", - "integrity": "sha512-ZToe+MbUF4lBqk6dV8GKot4DKfzrxXsplOddH8zN3YK+qw9/McvP7+4ICjZvOne0jQhN4eJwHsX6tT0Ns19fvw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-7.2.0.tgz", + "integrity": "sha512-0W4AcUxPpFlcS8ql8ZEmFwaI0X5WshUVAFdXe3PBurrt18DK8bvSS+UKHvJUAfGILco/nTtc/E4LcPNfVysfwQ==", "license": "MIT", "engines": { "node": ">=16" @@ -4920,13 +4777,6 @@ "dev": true, "license": "MIT" }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 67bd4ca..045fbb0 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "eslint-plugin-n": "^16.6.2", "eslint-plugin-promise": "^6.4.0", "mocha": "^10.6.0", - "node-mocks-http": "^1.15.0", "nodemon": "^3.1.4", "supertest": "^7.0.0" }, @@ -53,7 +52,7 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "module-alias": "^2.2.3", - "path-to-regexp": "^7.1.0", + "path-to-regexp": "^7.2.0", "swagger-jsdoc": "^6.2.8", "swagger-parser": "^10.0.3", "swagger-ui-express": "^5.0.1" From dd908b8454f05b2c264f34c8c852d172acc716d1 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 17:33:11 -0600 Subject: [PATCH 18/24] reduce resources used by kubernetes pod --- k8s/deployment.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index 56ef79d..3f92d93 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -7,8 +7,8 @@ metadata: spec: replicas: 2 # how many copies of each pod do we want? strategy: # how do we want to update the pods? - type: 'RollingUpdate' - rollingUpdate: + type: "RollingUpdate" + rollingUpdate: maxUnavailable: 50% maxSurge: 50% selector: # which pods are managed by this deployment? @@ -29,10 +29,10 @@ spec: imagePullPolicy: Always resources: limits: - memory: 2662Mi # 2.6 Gibibyte + memory: 666Mi # was 2.6 Gibibyte cpu: 500m # 0.5 CPU cores requests: - memory: 1228Mi # 1.2 Gibibyte + memory: 307Mi # was 1.2 Gibibyte cpu: 100m # 0.1 CPU cores ports: - containerPort: 8080 From 66c22ee5dde5098cfe51754600424437f183154c Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 17:42:25 -0600 Subject: [PATCH 19/24] issue with test workflow --- .github/workflows/test.yml | 1 + bin/github/workflows/test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bbb61a9..13b4b8d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,7 @@ jobs: - uses: jabez007/docker-kitchen/.github/shared/docker@master with: + image-name: ${{ github.event.repository.name }} tag: test prune: false debug: ${{ vars.pipeline_debug }} diff --git a/bin/github/workflows/test.yml b/bin/github/workflows/test.yml index bbb61a9..13b4b8d 100644 --- a/bin/github/workflows/test.yml +++ b/bin/github/workflows/test.yml @@ -36,6 +36,7 @@ jobs: - uses: jabez007/docker-kitchen/.github/shared/docker@master with: + image-name: ${{ github.event.repository.name }} tag: test prune: false debug: ${{ vars.pipeline_debug }} From 52a21536d1237f7c29e6b4bcb15578b7e37f3985 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 17:58:26 -0600 Subject: [PATCH 20/24] still trying to get test workflow to run --- .github/workflows/test.yml | 2 +- bin/github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13b4b8d..064868e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - name: Run Docker container id: container_run run: | - CONTAINER_ID=$(docker run -d -p 80:8888 ${{ github.repository }}:test | tail -n 1) + CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.repository }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - name: Test Docker container diff --git a/bin/github/workflows/test.yml b/bin/github/workflows/test.yml index 13b4b8d..064868e 100644 --- a/bin/github/workflows/test.yml +++ b/bin/github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - name: Run Docker container id: container_run run: | - CONTAINER_ID=$(docker run -d -p 80:8888 ${{ github.repository }}:test | tail -n 1) + CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.repository }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - name: Test Docker container From fc55054d6c4b48b774fd347a0b299123362700f7 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 18:11:43 -0600 Subject: [PATCH 21/24] use correct image name --- .github/workflows/test.yml | 2 +- bin/github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 064868e..be32e71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - name: Run Docker container id: container_run run: | - CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.repository }}:test | tail -n 1) + CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.event.repository.name }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - name: Test Docker container diff --git a/bin/github/workflows/test.yml b/bin/github/workflows/test.yml index 064868e..be32e71 100644 --- a/bin/github/workflows/test.yml +++ b/bin/github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - name: Run Docker container id: container_run run: | - CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.repository }}:test | tail -n 1) + CONTAINER_ID=$(docker run -d -p 80:8080 --env PORT=8080 ${{ github.event.repository.name }}:test | tail -n 1) echo "CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_OUTPUT" - name: Test Docker container From d17c9b90d048de1b8b9b97ed89fb97ed095ff884 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Sat, 4 Jan 2025 18:17:23 -0600 Subject: [PATCH 22/24] update image name for PR build --- .github/workflows/build.yml | 1 + bin/github/workflows/build.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1e5105..8fd8977 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,4 +19,5 @@ jobs: - uses: jabez007/docker-kitchen/.github/shared/docker@master with: + image-name: ${{ github.event.repository.name }} debug: ${{ vars.pipeline_debug }} diff --git a/bin/github/workflows/build.yml b/bin/github/workflows/build.yml index b1e5105..8fd8977 100644 --- a/bin/github/workflows/build.yml +++ b/bin/github/workflows/build.yml @@ -19,4 +19,5 @@ jobs: - uses: jabez007/docker-kitchen/.github/shared/docker@master with: + image-name: ${{ github.event.repository.name }} debug: ${{ vars.pipeline_debug }} From 6a11a27b5d1baf0f9b17cfeea929013bad23f76b Mon Sep 17 00:00:00 2001 From: jabez007 Date: Thu, 6 Feb 2025 08:42:33 -0600 Subject: [PATCH 23/24] handle graceful shutdown on more signals --- src/index.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 71ec20d..8f0e982 100644 --- a/src/index.js +++ b/src/index.js @@ -8,9 +8,17 @@ const server = app.listen(port, () => { console.log(`server started at http://localhost:${port}`) }) -process.on('SIGTERM', () => { - console.log('SIGTERM signal received, closing HTTP server') - server.close(() => { +const gracefulShutdown = () => { + console.log('Gracefully closing HTTP server') + server.close((err) => { + if (err) { + console.error(`Error closing HTTP server: ${err}`) + process.exit(1) + } console.log('HTTP server closed') + process.exit(0) }) -}) +} + +process.on('SIGINT', gracefulShutdown) +process.on('SIGTERM', gracefulShutdown) From d36dadc744c7c0d11440a7a9dfc921a1eba99fd1 Mon Sep 17 00:00:00 2001 From: jabez007 Date: Fri, 21 Mar 2025 17:18:56 -0500 Subject: [PATCH 24/24] add startup probe --- k8s/deployment.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index 3f92d93..e914de9 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -44,10 +44,18 @@ spec: path: /ping port: 8080 initialDelaySeconds: 15 - periodSeconds: 10 + periodSeconds: 15 readinessProbe: # checks whether the container is ready to receive traffic httpGet: path: /ping port: 8080 initialDelaySeconds: 5 periodSeconds: 5 + startupProbe: # hold off liveness and readiness probes until container is started + httpGet: + path: /ping + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 10 + failureThreshold: 10 + failure