From e0afaec8bba03ca6a75706dfe02e9c47af480e74 Mon Sep 17 00:00:00 2001 From: feliperm17 Date: Sat, 16 May 2026 13:02:45 -0300 Subject: [PATCH 1/5] =?UTF-8?q?Nova=20Tabela=20e=20Migration:=20Cria=C3=A7?= =?UTF-8?q?=C3=A3o=20da=20tabela=20splinker=5Flogs=20contendo=20os=20campo?= =?UTF-8?q?s=20id,=20data=5Fhora,=20max=5Ftombo=5Fhcf,=20sucesso=20(boolea?= =?UTF-8?q?no)=20e=20log=5Fsaida=20(texto)=20para=20ter=20hist=C3=B3rico?= =?UTF-8?q?=20de=20auditoria=20das=20execu=C3=A7=C3=B5es=20do=20JAR.=20Ade?= =?UTF-8?q?qua=C3=A7=C3=A3o=20da=20Imagem=20Docker:=20O=20Dockerfile=20da?= =?UTF-8?q?=20pasta=20splinker=20foi=20modificado=20para=20instalar=20tamb?= =?UTF-8?q?=C3=A9m=20o=20ecossistema=20do=20Node.js=20e=20o=20pacote=20pg.?= =?UTF-8?q?=20Isso=20garante=20que=20o=20container=20que=20roda=20o=20java?= =?UTF-8?q?=20tamb=C3=A9m=20seja=20capaz=20de=20executar=20nossa=20ponte?= =?UTF-8?q?=20de=20captura=20em=20JS.=20Script=20de=20Ingest=C3=A3o=20(sav?= =?UTF-8?q?e=5Flogs.js):=20Cria=C3=A7=C3=A3o=20de=20um=20script=20Node=20f?= =?UTF-8?q?ocado=20em=20ler=20de=20forma=20ass=C3=ADncrona=20a=20entrada?= =?UTF-8?q?=20de=20fluxo=20(process.stdin).=20Ap=C3=B3s=20receber=20todo?= =?UTF-8?q?=20o=20payload,=20ele=20avalia=20as=20express=C3=B5es=20de=20su?= =?UTF-8?q?cesso=20no=20log,=20conecta=20ao=20PostgreSQL=20usando=20as=20c?= =?UTF-8?q?redenciais=20vigentes,=20faz=20a=20query=20do=20MAX(hcf)=20da?= =?UTF-8?q?=20tabela=20tombos=20e=20persiste=20o=20relat=C3=B3rio=20final?= =?UTF-8?q?=20na=20nova=20tabela=20de=20logs.=20Pipeline=20via=20Bash=20(r?= =?UTF-8?q?un=5Fsplinker.sh):=20Para=20n=C3=A3o=20sujar=20o=20cron,=20cria?= =?UTF-8?q?mos=20um=20shell=20script=20dedicado=20que=20basicamente=20roda?= =?UTF-8?q?=20o=20comando=20Java=20e=20faz=20o=20pipe=20da=20sa=C3=ADda=20?= =?UTF-8?q?(stdout=20e=20stderr)=20direto=20para=20o=20script=20Node=20(ja?= =?UTF-8?q?va=20-jar=20...=20|=20node=20save=5Flogs.js).=20Resolu=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Escopo=20do=20Cron:=20Como=20daemons=20de=20cron?= =?UTF-8?q?=20limpam=20o=20contexto=20das=20vari=C3=A1veis=20de=20ambiente?= =?UTF-8?q?,=20o=20entrypoint.sh=20foi=20atualizado=20para=20extrair=20as?= =?UTF-8?q?=20vari=C3=A1veis=20do=20banco=20(do=20.env=20injetado=20pelo?= =?UTF-8?q?=20Docker)=20em=20um=20arquivo=20acess=C3=ADvel=20que=20o=20she?= =?UTF-8?q?ll=20script=20possa=20dar=20source=20antes=20de=20rodar=20o=20N?= =?UTF-8?q?ode.js.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- splinker/Dockerfile | 10 +++- splinker/entrypoint.sh | 8 ++- splinker/run_splinker.sh | 5 ++ splinker/save_logs.js | 49 +++++++++++++++++++ ...0260513003253_cria_tabela_splinker_logs.ts | 14 ++++++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 splinker/run_splinker.sh create mode 100644 splinker/save_logs.js create mode 100644 src/database/migration/20260513003253_cria_tabela_splinker_logs.ts diff --git a/splinker/Dockerfile b/splinker/Dockerfile index e15cba2..9c43b85 100644 --- a/splinker/Dockerfile +++ b/splinker/Dockerfile @@ -9,18 +9,26 @@ RUN \ apt-get install -y \ cron \ tzdata \ - curl && \ + curl \ + nodejs \ + npm && \ curl -fsSL https://github.com/cria/splinker-javafx/releases/download/v${SPLINKER_VERSION}/splinker.jar \ -o /app/splinker.jar && \ apt-get remove -y curl && \ rm -rf /var/lib/apt/lists/* +RUN npm init -y && npm install pg + ENV \ TZ=America/Sao_Paulo \ CRON_SCHEDULE="0 0 * * *" RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +COPY save_logs.js /app/save_logs.js +COPY run_splinker.sh /app/run_splinker.sh +RUN chmod +x /app/run_splinker.sh + COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh diff --git a/splinker/entrypoint.sh b/splinker/entrypoint.sh index 07eae8f..491e6d8 100644 --- a/splinker/entrypoint.sh +++ b/splinker/entrypoint.sh @@ -11,9 +11,15 @@ user=${DATABASE_USERNAME} password=${DATABASE_PASSWORD} EOF +echo "export DATABASE_HOST='${DATABASE_HOST}'" > /app/env.sh +echo "export DATABASE_PORT='${DATABASE_PORT}'" >> /app/env.sh +echo "export DATABASE_NAME='${DATABASE_NAME}'" >> /app/env.sh +echo "export DATABASE_USERNAME='${DATABASE_USERNAME}'" >> /app/env.sh +echo "export DATABASE_PASSWORD='${DATABASE_PASSWORD}'" >> /app/env.sh + echo "Configuring cron job with schedule: $CRON_SCHEDULE ($TZ)" -echo "$CRON_SCHEDULE $(which java) -jar /app/splinker.jar /app/splinker.conf 1> /proc/1/fd/1 2> /proc/1/fd/2" | crontab - +echo "$CRON_SCHEDULE /app/run_splinker.sh 1> /proc/1/fd/1 2> /proc/1/fd/2" | crontab - if ! crontab -l >/dev/null 2>&1; then echo "Failed to configure cron job" diff --git a/splinker/run_splinker.sh b/splinker/run_splinker.sh new file mode 100644 index 0000000..4538819 --- /dev/null +++ b/splinker/run_splinker.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# Roda o jar do splinker e redireciona todo o output para o script node +. /app/env.sh +$(which java) -jar /app/splinker.jar /app/splinker.conf 2>&1 | node /app/save_logs.js diff --git a/splinker/save_logs.js b/splinker/save_logs.js new file mode 100644 index 0000000..1b683d3 --- /dev/null +++ b/splinker/save_logs.js @@ -0,0 +1,49 @@ +const { Client } = require('pg'); + +async function main() { + // Ler os logs do stdin + let logData = ''; + process.stdin.setEncoding('utf8'); + + for await (const chunk of process.stdin) { + logData += chunk; + } + + // Determinar se houve sucesso + const isSuccess = logData.includes('Transmission completed successfully') || + /Success:\s*[1-9]/.test(logData); + + // Configurar conexão com o banco de dados + const client = new Client({ + host: process.env.DATABASE_HOST, + port: process.env.DATABASE_PORT, + database: process.env.DATABASE_NAME, + user: process.env.DATABASE_USERNAME, + password: process.env.DATABASE_PASSWORD, + }); + + try { + await client.connect(); + + // Buscar max_tombo_hcf + const res = await client.query('SELECT MAX(hcf) as max_hcf FROM tombos'); + const maxTomboHcf = res.rows[0].max_hcf; + + // Inserir os logs + const insertQuery = ` + INSERT INTO splinker_logs (max_tombo_hcf, sucesso, log_saida) + VALUES ($1, $2, $3) + `; + await client.query(insertQuery, [maxTomboHcf, isSuccess, logData]); + + console.log('Logs salvos com sucesso na tabela splinker_logs.'); + + } catch (error) { + console.error('Erro ao salvar os logs:', error); + process.exit(1); + } finally { + await client.end(); + } +} + +main().catch(console.error); diff --git a/src/database/migration/20260513003253_cria_tabela_splinker_logs.ts b/src/database/migration/20260513003253_cria_tabela_splinker_logs.ts new file mode 100644 index 0000000..31e10fc --- /dev/null +++ b/src/database/migration/20260513003253_cria_tabela_splinker_logs.ts @@ -0,0 +1,14 @@ +import { Knex } from 'knex' + +export async function run(knex: Knex): Promise { + const hasTable = await knex.schema.hasTable('splinker_logs') + if (!hasTable) { + await knex.schema.createTable('splinker_logs', table => { + table.increments('id').primary() + table.timestamp('data_hora').defaultTo(knex.fn.now()) + table.integer('max_tombo_hcf') + table.boolean('sucesso') + table.text('log_saida') + }) + } +} From 332dedc85eb7cd00ef54e5b11c6ebfa7096f4cbc Mon Sep 17 00:00:00 2001 From: feliperm17 Date: Sat, 23 May 2026 11:31:48 -0300 Subject: [PATCH 2/5] funcionando --- splinker/compose.yml | 11 ++++++++++- splinker/entrypoint.sh | 12 ++++++++++++ splinker/save_logs.js | 10 +++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/splinker/compose.yml b/splinker/compose.yml index 6b66e62..130bc72 100644 --- a/splinker/compose.yml +++ b/splinker/compose.yml @@ -3,4 +3,13 @@ services: image: splinker:local container_name: splinker build: . - env_file: .env + env_file: ../.env + networks: + - hcf-api_herbario_network + - conversao_banco_python_my_network + +networks: + hcf-api_herbario_network: + external: true + conversao_banco_python_my_network: + external: true diff --git a/splinker/entrypoint.sh b/splinker/entrypoint.sh index 491e6d8..54285dc 100644 --- a/splinker/entrypoint.sh +++ b/splinker/entrypoint.sh @@ -1,6 +1,18 @@ #!/bin/sh echo "Generating config from environment variables..." + +DATABASE_HOST="${DATABASE_HOST:-$PG_HOST}" +DATABASE_PORT="${DATABASE_PORT:-$PG_PORT}" +DATABASE_NAME="${DATABASE_NAME:-$PG_DATABASE}" +DATABASE_USERNAME="${DATABASE_USERNAME:-$PG_USERNAME}" +DATABASE_PASSWORD="${DATABASE_PASSWORD:-$PG_PASSWORD}" + +# Dentro do Docker, substituir localhost por nome do container +if [ "$DATABASE_HOST" = "localhost" ] || [ "$DATABASE_HOST" = "127.0.0.1" ]; then + DATABASE_HOST="hcf_postgres" +fi + cat > /app/splinker.conf < Date: Sat, 23 May 2026 16:50:32 -0300 Subject: [PATCH 3/5] js -> ts --- splinker/Dockerfile | 4 +- splinker/run_splinker.sh | 4 +- splinker/save_logs.js | 49 --------------------- splinker/save_logs.ts | 92 ++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 1 + 5 files changed, 97 insertions(+), 53 deletions(-) delete mode 100644 splinker/save_logs.js create mode 100644 splinker/save_logs.ts diff --git a/splinker/Dockerfile b/splinker/Dockerfile index 9c43b85..e9648f5 100644 --- a/splinker/Dockerfile +++ b/splinker/Dockerfile @@ -17,7 +17,7 @@ RUN \ apt-get remove -y curl && \ rm -rf /var/lib/apt/lists/* -RUN npm init -y && npm install pg +RUN npm init -y && npm install pg tsx ENV \ TZ=America/Sao_Paulo \ @@ -25,7 +25,7 @@ ENV \ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -COPY save_logs.js /app/save_logs.js +COPY save_logs.ts /app/save_logs.ts COPY run_splinker.sh /app/run_splinker.sh RUN chmod +x /app/run_splinker.sh diff --git a/splinker/run_splinker.sh b/splinker/run_splinker.sh index 4538819..2ba9a0d 100644 --- a/splinker/run_splinker.sh +++ b/splinker/run_splinker.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Roda o jar do splinker e redireciona todo o output para o script node +# Roda o jar do splinker e redireciona todo o output para o script TypeScript . /app/env.sh -$(which java) -jar /app/splinker.jar /app/splinker.conf 2>&1 | node /app/save_logs.js +$(which java) -jar /app/splinker.jar /app/splinker.conf 2>&1 | npx tsx /app/save_logs.ts diff --git a/splinker/save_logs.js b/splinker/save_logs.js deleted file mode 100644 index 8e83897..0000000 --- a/splinker/save_logs.js +++ /dev/null @@ -1,49 +0,0 @@ -const { Client } = require('pg'); - -async function main() { - // Ler os logs do stdin - let logData = ''; - process.stdin.setEncoding('utf8'); - - for await (const chunk of process.stdin) { - logData += chunk; - } - - // Determinar se houve sucesso - const isSuccess = logData.includes('Transmission completed successfully') || - /Success:\s*[1-9]/.test(logData); - - // Configurar conexão com o banco de dados - const client = new Client({ - host: process.env.DATABASE_HOST || process.env.PG_HOST, - port: process.env.DATABASE_PORT || process.env.PG_PORT, - database: process.env.DATABASE_NAME || process.env.PG_DATABASE, - user: process.env.DATABASE_USERNAME || process.env.PG_USERNAME, - password: process.env.DATABASE_PASSWORD || process.env.PG_PASSWORD, - }); - - try { - await client.connect(); - - // Buscar max_tombo_hcf - const res = await client.query('SELECT MAX(hcf) as max_hcf FROM tombos'); - const maxTomboHcf = res.rows[0].max_hcf; - - // Inserir os logs - const insertQuery = ` - INSERT INTO splinker_logs (max_tombo_hcf, sucesso, log_saida) - VALUES ($1, $2, $3) - `; - await client.query(insertQuery, [maxTomboHcf, isSuccess, logData]); - - console.log('Logs salvos com sucesso na tabela splinker_logs.'); - - } catch (error) { - console.error('Erro ao salvar os logs:', error); - process.exit(1); - } finally { - await client.end(); - } -} - -main().catch(console.error); diff --git a/splinker/save_logs.ts b/splinker/save_logs.ts new file mode 100644 index 0000000..5740098 --- /dev/null +++ b/splinker/save_logs.ts @@ -0,0 +1,92 @@ +/* eslint-disable no-console */ + +import { Client } from 'pg' + +const DATABASE_HOST = process.env.DATABASE_HOST ?? process.env.PG_HOST ?? 'hcf_postgres' +const DATABASE_PORT = process.env.DATABASE_PORT ?? process.env.PG_PORT ?? '5432' +const DATABASE_NAME = process.env.DATABASE_NAME ?? process.env.PG_DATABASE +const DATABASE_USERNAME = process.env.DATABASE_USERNAME ?? process.env.PG_USERNAME +const DATABASE_PASSWORD = process.env.DATABASE_PASSWORD ?? process.env.PG_PASSWORD + +async function readStdin(): Promise { + let result = '' + process.stdin.setEncoding('utf8') + + for await (const chunk of process.stdin) { + result += chunk + } + + return result +} + +function parseSuccess(logData: string): boolean { + return ( + logData.includes('Transmission completed successfully') + || /Success:\s*[1-9]/.test(logData) + ) +} + +function createClient(): Client { + if (!DATABASE_NAME || !DATABASE_USERNAME || !DATABASE_PASSWORD) { + throw new Error('Database connection variables are not fully provided.') + } + + return new Client({ + host: DATABASE_HOST, + port: parseInt(DATABASE_PORT, 10), + database: DATABASE_NAME, + user: DATABASE_USERNAME, + password: DATABASE_PASSWORD + }) +} + +async function main(): Promise { + const logData = await readStdin() + const sucesso = parseSuccess(logData) + const client = createClient() + + try { + await client.connect() + + const res = await client.query<{ max_hcf: number | null }>( + 'SELECT MAX(hcf) as max_hcf FROM tombos' + ) + const maxTomboHcf = res.rows[0]?.max_hcf ?? null + + const insertQuery = ` + INSERT INTO splinker_logs (max_tombo_hcf, sucesso, log_saida) + VALUES ($1, $2, $3) + ` + + await client.query( + insertQuery, + [ + maxTomboHcf, + sucesso, + logData + ] + ) + + console.log('Logs salvos com sucesso na tabela splinker_logs.') + } catch (error: unknown) { + if (error instanceof Error) { + console.error('Erro ao salvar os logs:', error.message) + } else { + console.error('Erro ao salvar os logs:', error) + } + + process.exit(1) + } finally { + await client.end() + } +} + +main().catch((error: unknown) => { + if (error instanceof Error) { + console.error(error.message) + } else { + console.error(error) + } + + process.exit(1) +}) diff --git a/tsconfig.json b/tsconfig.json index cf1cdce..c1b02ab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,6 +36,7 @@ "include": [ "src/", "test/", + "splinker/", "eslint.config.mts", "vitest.config.mts" ], From d8238ac76a5f8ef3130160bc849f0ca7f6e4d897 Mon Sep 17 00:00:00 2001 From: feliperm17 Date: Thu, 4 Jun 2026 15:02:44 -0300 Subject: [PATCH 4/5] =?UTF-8?q?Corre=C3=A7oes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintignore | 1 + eslint.config.mts | 3 +- splinker/Dockerfile | 22 ++- splinker/compose.yml | 13 +- splinker/entrypoint.sh | 25 +-- splinker/package-lock.json | 161 ++++++++++++++++++ splinker/package.json | 8 + splinker/run_splinker.sh | 7 +- splinker/save_logs.mjs | 81 +++++++++ splinker/save_logs.ts | 92 ---------- ...ts => 20260530_cria_splinker_execucoes.ts} | 6 +- tsconfig.json | 1 - 12 files changed, 286 insertions(+), 134 deletions(-) create mode 100644 .eslintignore create mode 100644 splinker/package-lock.json create mode 100644 splinker/package.json create mode 100644 splinker/save_logs.mjs delete mode 100644 splinker/save_logs.ts rename src/database/migration/{20260513003253_cria_tabela_splinker_logs.ts => 20260530_cria_splinker_execucoes.ts} (61%) diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..a1413e5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +splinker/ diff --git a/eslint.config.mts b/eslint.config.mts index d88d489..09cf78b 100644 --- a/eslint.config.mts +++ b/eslint.config.mts @@ -14,7 +14,8 @@ export default defineConfig([ 'mysql/', 'public/', 'coverage/', - 'node_modules/' + 'node_modules/', + 'splinker/' ] }, { diff --git a/splinker/Dockerfile b/splinker/Dockerfile index e9648f5..3c4729d 100644 --- a/splinker/Dockerfile +++ b/splinker/Dockerfile @@ -1,23 +1,30 @@ FROM eclipse-temurin:21-jre ARG SPLINKER_VERSION=7.2.7 +ARG NODE_VERSION=22.20.0 WORKDIR /app -RUN \ - apt-get update && \ +# Install minimal packages, download splinker.jar and Node.js binary +RUN apt-get update && \ apt-get install -y \ cron \ tzdata \ curl \ - nodejs \ - npm && \ + xz-utils \ + ca-certificates && \ curl -fsSL https://github.com/cria/splinker-javafx/releases/download/v${SPLINKER_VERSION}/splinker.jar \ -o /app/splinker.jar && \ - apt-get remove -y curl && \ + curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz -o /tmp/node.tar.xz && \ + tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 && \ + rm -f /tmp/node.tar.xz && \ + apt-get remove -y curl xz-utils && \ + apt autoremove -y && \ rm -rf /var/lib/apt/lists/* -RUN npm init -y && npm install pg tsx +# Copy package.json and package-lock.json from splinker/ and install dependencies deterministically +COPY package.json package-lock.json /app/ +RUN npm ci --production ENV \ TZ=America/Sao_Paulo \ @@ -25,8 +32,7 @@ ENV \ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -COPY save_logs.ts /app/save_logs.ts -COPY run_splinker.sh /app/run_splinker.sh +COPY save_logs.mjs run_splinker.sh /app/ RUN chmod +x /app/run_splinker.sh COPY entrypoint.sh /entrypoint.sh diff --git a/splinker/compose.yml b/splinker/compose.yml index 130bc72..93ae4b3 100644 --- a/splinker/compose.yml +++ b/splinker/compose.yml @@ -3,13 +3,6 @@ services: image: splinker:local container_name: splinker build: . - env_file: ../.env - networks: - - hcf-api_herbario_network - - conversao_banco_python_my_network - -networks: - hcf-api_herbario_network: - external: true - conversao_banco_python_my_network: - external: true + env_file: .env + extra_hosts: + - "host.docker.internal:host-gateway" diff --git a/splinker/entrypoint.sh b/splinker/entrypoint.sh index 54285dc..a3e09da 100644 --- a/splinker/entrypoint.sh +++ b/splinker/entrypoint.sh @@ -1,17 +1,16 @@ #!/bin/sh -echo "Generating config from environment variables..." +# Export container environment variables to /etc/environment so cron can read them +printenv | grep -v "^\(HOME\|USER\|LOGNAME\|SHELL\|PATH\)=" >> /etc/environment -DATABASE_HOST="${DATABASE_HOST:-$PG_HOST}" -DATABASE_PORT="${DATABASE_PORT:-$PG_PORT}" -DATABASE_NAME="${DATABASE_NAME:-$PG_DATABASE}" -DATABASE_USERNAME="${DATABASE_USERNAME:-$PG_USERNAME}" -DATABASE_PASSWORD="${DATABASE_PASSWORD:-$PG_PASSWORD}" +echo "Generating config from environment variables..." -# Dentro do Docker, substituir localhost por nome do container -if [ "$DATABASE_HOST" = "localhost" ] || [ "$DATABASE_HOST" = "127.0.0.1" ]; then - DATABASE_HOST="hcf_postgres" -fi +# Expect DATABASE_* variables to be provided by the environment/container +: "${DATABASE_HOST:?DATABASE_HOST is required}" +: "${DATABASE_PORT:?DATABASE_PORT is required}" +: "${DATABASE_NAME:?DATABASE_NAME is required}" +: "${DATABASE_USERNAME:?DATABASE_USERNAME is required}" +: "${DATABASE_PASSWORD:?DATABASE_PASSWORD is required}" cat > /app/splinker.conf < /app/env.sh -echo "export DATABASE_PORT='${DATABASE_PORT}'" >> /app/env.sh -echo "export DATABASE_NAME='${DATABASE_NAME}'" >> /app/env.sh -echo "export DATABASE_USERNAME='${DATABASE_USERNAME}'" >> /app/env.sh -echo "export DATABASE_PASSWORD='${DATABASE_PASSWORD}'" >> /app/env.sh - echo "Configuring cron job with schedule: $CRON_SCHEDULE ($TZ)" echo "$CRON_SCHEDULE /app/run_splinker.sh 1> /proc/1/fd/1 2> /proc/1/fd/2" | crontab - diff --git a/splinker/package-lock.json b/splinker/package-lock.json new file mode 100644 index 0000000..aa0138d --- /dev/null +++ b/splinker/package-lock.json @@ -0,0 +1,161 @@ +{ + "name": "splinker-helper", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "splinker-helper", + "version": "0.1.0", + "dependencies": { + "pg": "^8.16.3" + } + }, + "node_modules/pg": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", + "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", + "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", + "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/splinker/package.json b/splinker/package.json new file mode 100644 index 0000000..c382272 --- /dev/null +++ b/splinker/package.json @@ -0,0 +1,8 @@ +{ + "name": "splinker-helper", + "version": "0.1.0", + "private": true, + "dependencies": { + "pg": "^8.16.3" + } +} diff --git a/splinker/run_splinker.sh b/splinker/run_splinker.sh index 2ba9a0d..460f968 100644 --- a/splinker/run_splinker.sh +++ b/splinker/run_splinker.sh @@ -1,5 +1,6 @@ #!/bin/sh -# Roda o jar do splinker e redireciona todo o output para o script TypeScript -. /app/env.sh -$(which java) -jar /app/splinker.jar /app/splinker.conf 2>&1 | npx tsx /app/save_logs.ts +# Roda o jar do splinker, duplica o output para os logs do Docker e envia para o script de log +# Usamos tee para que a saída também seja registrada nos logs do container +# Caminhos absolutos são necessários pois o cron tem um PATH mínimo +/opt/java/openjdk/bin/java -jar /app/splinker.jar /app/splinker.conf 2>&1 | tee /proc/1/fd/1 | /usr/local/bin/node /app/save_logs.mjs diff --git a/splinker/save_logs.mjs b/splinker/save_logs.mjs new file mode 100644 index 0000000..d91183a --- /dev/null +++ b/splinker/save_logs.mjs @@ -0,0 +1,81 @@ +import { Client } from 'pg' + +const DATABASE_HOST = process.env.DATABASE_HOST +const DATABASE_PORT = process.env.DATABASE_PORT +const DATABASE_NAME = process.env.DATABASE_NAME +const DATABASE_USERNAME = process.env.DATABASE_USERNAME +const DATABASE_PASSWORD = process.env.DATABASE_PASSWORD + +async function readStdin() { + let result = '' + process.stdin.setEncoding('utf8') + + for await (const chunk of process.stdin) { + result += chunk + } + + return result +} + +function parseSuccess(logData) { + return ( + logData.includes('Transmission completed successfully') + || /Success:\s*[1-9]/.test(logData) + ) +} + +function createClient() { + if (!DATABASE_NAME || !DATABASE_USERNAME || !DATABASE_PASSWORD) { + throw new Error('Database connection variables are not fully provided.') + } + + return new Client({ + host: DATABASE_HOST, + port: parseInt(DATABASE_PORT, 10), + database: DATABASE_NAME, + user: DATABASE_USERNAME, + password: DATABASE_PASSWORD + }) +} + +async function main() { + const logData = await readStdin() + const sucesso = parseSuccess(logData) + const client = createClient() + + try { + await client.connect() + await client.query("SET timezone TO 'America/Sao_Paulo'") + + const res = await client.query( + 'SELECT MAX("CatalogNumber") as ultimo_tombo_hcf FROM vw_splinker' + ) + const ultimoTomboHcf = res.rows[0]?.ultimo_tombo_hcf ?? null + + const insertQuery = ` + INSERT INTO splinker_execucoes (ultimo_tombo_hcf, sucesso, log_saida) + VALUES ($1, $2, $3) + ` + + await client.query( + insertQuery, + [ + ultimoTomboHcf, + sucesso, + logData + ] + ) + + console.log('Execução do Splinker registrada em splinker_execucoes.') + } catch (error) { + console.error('Erro ao salvar os logs:', error instanceof Error ? error.message : error) + process.exit(1) + } finally { + await client.end() + } +} + +main().catch((error) => { + console.error(error instanceof Error ? error.message : error) + process.exit(1) +}) diff --git a/splinker/save_logs.ts b/splinker/save_logs.ts deleted file mode 100644 index 5740098..0000000 --- a/splinker/save_logs.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* eslint-disable no-console */ - -import { Client } from 'pg' - -const DATABASE_HOST = process.env.DATABASE_HOST ?? process.env.PG_HOST ?? 'hcf_postgres' -const DATABASE_PORT = process.env.DATABASE_PORT ?? process.env.PG_PORT ?? '5432' -const DATABASE_NAME = process.env.DATABASE_NAME ?? process.env.PG_DATABASE -const DATABASE_USERNAME = process.env.DATABASE_USERNAME ?? process.env.PG_USERNAME -const DATABASE_PASSWORD = process.env.DATABASE_PASSWORD ?? process.env.PG_PASSWORD - -async function readStdin(): Promise { - let result = '' - process.stdin.setEncoding('utf8') - - for await (const chunk of process.stdin) { - result += chunk - } - - return result -} - -function parseSuccess(logData: string): boolean { - return ( - logData.includes('Transmission completed successfully') - || /Success:\s*[1-9]/.test(logData) - ) -} - -function createClient(): Client { - if (!DATABASE_NAME || !DATABASE_USERNAME || !DATABASE_PASSWORD) { - throw new Error('Database connection variables are not fully provided.') - } - - return new Client({ - host: DATABASE_HOST, - port: parseInt(DATABASE_PORT, 10), - database: DATABASE_NAME, - user: DATABASE_USERNAME, - password: DATABASE_PASSWORD - }) -} - -async function main(): Promise { - const logData = await readStdin() - const sucesso = parseSuccess(logData) - const client = createClient() - - try { - await client.connect() - - const res = await client.query<{ max_hcf: number | null }>( - 'SELECT MAX(hcf) as max_hcf FROM tombos' - ) - const maxTomboHcf = res.rows[0]?.max_hcf ?? null - - const insertQuery = ` - INSERT INTO splinker_logs (max_tombo_hcf, sucesso, log_saida) - VALUES ($1, $2, $3) - ` - - await client.query( - insertQuery, - [ - maxTomboHcf, - sucesso, - logData - ] - ) - - console.log('Logs salvos com sucesso na tabela splinker_logs.') - } catch (error: unknown) { - if (error instanceof Error) { - console.error('Erro ao salvar os logs:', error.message) - } else { - console.error('Erro ao salvar os logs:', error) - } - - process.exit(1) - } finally { - await client.end() - } -} - -main().catch((error: unknown) => { - if (error instanceof Error) { - console.error(error.message) - } else { - console.error(error) - } - - process.exit(1) -}) diff --git a/src/database/migration/20260513003253_cria_tabela_splinker_logs.ts b/src/database/migration/20260530_cria_splinker_execucoes.ts similarity index 61% rename from src/database/migration/20260513003253_cria_tabela_splinker_logs.ts rename to src/database/migration/20260530_cria_splinker_execucoes.ts index 31e10fc..0254e16 100644 --- a/src/database/migration/20260513003253_cria_tabela_splinker_logs.ts +++ b/src/database/migration/20260530_cria_splinker_execucoes.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex' export async function run(knex: Knex): Promise { - const hasTable = await knex.schema.hasTable('splinker_logs') + const hasTable = await knex.schema.hasTable('splinker_execucoes') if (!hasTable) { - await knex.schema.createTable('splinker_logs', table => { + await knex.schema.createTable('splinker_execucoes', table => { table.increments('id').primary() table.timestamp('data_hora').defaultTo(knex.fn.now()) - table.integer('max_tombo_hcf') + table.integer('ultimo_tombo_hcf') table.boolean('sucesso') table.text('log_saida') }) diff --git a/tsconfig.json b/tsconfig.json index c1b02ab..cf1cdce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,7 +36,6 @@ "include": [ "src/", "test/", - "splinker/", "eslint.config.mts", "vitest.config.mts" ], From 24afdaf9f617cb84a825e99a1bd4005fcded85d0 Mon Sep 17 00:00:00 2001 From: feliperm17 Date: Tue, 9 Jun 2026 21:01:34 -0300 Subject: [PATCH 5/5] Conversao Yarn --- splinker/Dockerfile | 8 +- splinker/package-lock.json | 161 ------------------------------------- splinker/yarn.lock | 91 +++++++++++++++++++++ 3 files changed, 96 insertions(+), 164 deletions(-) delete mode 100644 splinker/package-lock.json create mode 100644 splinker/yarn.lock diff --git a/splinker/Dockerfile b/splinker/Dockerfile index 3c4729d..749e389 100644 --- a/splinker/Dockerfile +++ b/splinker/Dockerfile @@ -18,13 +18,15 @@ RUN apt-get update && \ curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz -o /tmp/node.tar.xz && \ tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 && \ rm -f /tmp/node.tar.xz && \ + corepack enable && \ apt-get remove -y curl xz-utils && \ apt autoremove -y && \ rm -rf /var/lib/apt/lists/* -# Copy package.json and package-lock.json from splinker/ and install dependencies deterministically -COPY package.json package-lock.json /app/ -RUN npm ci --production +# Copy package.json and yarn.lock from splinker/ and install dependencies +COPY package.json yarn.lock /app/ +RUN yarn install --production && \ + yarn cache clean --force ENV \ TZ=America/Sao_Paulo \ diff --git a/splinker/package-lock.json b/splinker/package-lock.json deleted file mode 100644 index aa0138d..0000000 --- a/splinker/package-lock.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "name": "splinker-helper", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "splinker-helper", - "version": "0.1.0", - "dependencies": { - "pg": "^8.16.3" - } - }, - "node_modules/pg": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", - "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.13.0", - "pg-pool": "^3.14.0", - "pg-protocol": "^1.14.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.4.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", - "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", - "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", - "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", - "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/splinker/yarn.lock b/splinker/yarn.lock new file mode 100644 index 0000000..d5e66ed --- /dev/null +++ b/splinker/yarn.lock @@ -0,0 +1,91 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +pg-cloudflare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz#4b4c20e6d8ae531d400730f4804571a8d62f1497" + integrity sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A== + +pg-connection-string@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.13.0.tgz#8678113465a5af3cc977dcb51eadc847b27aa2de" + integrity sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig== + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.14.0.tgz#f35ae4eb846780cad71af24099b3edfa9781ad90" + integrity sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw== + +pg-protocol@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.14.0.tgz#c1f045b74274b007078c687147141f785f59b8de" + integrity sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA== + +pg-types@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@^8.16.3: + version "8.21.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.21.0.tgz#d7fa2118d960cec5cc7d2b24525f9850dd5932b0" + integrity sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA== + dependencies: + pg-connection-string "^2.13.0" + pg-pool "^3.14.0" + pg-protocol "^1.14.0" + pg-types "2.2.0" + pgpass "1.0.5" + optionalDependencies: + pg-cloudflare "^1.4.0" + +pgpass@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" + integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== + dependencies: + split2 "^4.1.0" + +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.1.tgz#c40b3da0222c500ff1e51c5d7014b60b79697c7a" + integrity sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ== + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +split2@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==