seguimos confiando en codex
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
This commit is contained in:
@@ -49,25 +49,25 @@
|
|||||||
label: 'No hay lotes registrados'
|
label: 'No hay lotes registrados'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #codigo-cell="{ row }">
|
<template #codigo-cell="{ getValue }">
|
||||||
<span class="font-mono font-semibold">{{ row.codigo || '-' }}</span>
|
<span class="font-mono font-semibold">{{ getValue() || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #tipo-cell="{ row }">
|
<template #tipo-cell="{ getValue }">
|
||||||
<UBadge :color="getTipoColor(row.tipo)" variant="subtle">
|
<UBadge :color="getTipoColor(getValue())" variant="subtle">
|
||||||
{{ getTipoLabel(row.tipo) }}
|
{{ getTipoLabel(getValue()) }}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cantidad_kg-cell="{ row }">
|
<template #cantidad_kg-cell="{ getValue }">
|
||||||
<span v-if="row.cantidad_kg" class="font-medium">
|
<span v-if="getValue()" class="font-medium">
|
||||||
{{ row.cantidad_kg.toLocaleString('es-AR') }} kg
|
{{ getValue().toLocaleString('es-AR') }} kg
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-gray-400">-</span>
|
<span v-else class="text-gray-400">-</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #fecha_creado-cell="{ row }">
|
<template #fecha_creado-cell="{ getValue }">
|
||||||
{{ formatDate(row.fecha_creado) }}
|
{{ formatDate(getValue()) }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #actions-cell="{ row }">
|
<template #actions-cell="{ row }">
|
||||||
|
|||||||
@@ -49,17 +49,17 @@
|
|||||||
label: 'No hay operaciones registradas'
|
label: 'No hay operaciones registradas'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #tipo-cell="{ row }">
|
<template #tipo-cell="{ row, getValue }">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<UIcon :name="getTipoIcon(row.tipo)" class="w-4 h-4" />
|
<UIcon :name="getTipoIcon(getValue())" class="w-4 h-4" />
|
||||||
<UBadge :color="getTipoColor(row.tipo)" variant="subtle">
|
<UBadge :color="getTipoColor(getValue())" variant="subtle">
|
||||||
{{ getTipoLabel(row.tipo) }}
|
{{ getTipoLabel(getValue()) }}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #fecha-cell="{ row }">
|
<template #fecha-cell="{ getValue }">
|
||||||
{{ formatDate(row.fecha) }}
|
{{ formatDate(getValue()) }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #actions-cell="{ row }">
|
<template #actions-cell="{ row }">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useRuntimeConfig } from '#imports'
|
|||||||
const { Pool } = pg
|
const { Pool } = pg
|
||||||
|
|
||||||
let pool: pg.Pool | null = null
|
let pool: pg.Pool | null = null
|
||||||
|
let connectionStringLogged = false
|
||||||
|
|
||||||
function buildConnectionString(): string {
|
function buildConnectionString(): string {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
@@ -28,6 +29,12 @@ export function getPool(): pg.Pool {
|
|||||||
if (!pool) {
|
if (!pool) {
|
||||||
const connectionString = buildConnectionString()
|
const connectionString = buildConnectionString()
|
||||||
|
|
||||||
|
if (!connectionStringLogged && process.env.NODE_ENV !== 'production') {
|
||||||
|
const masked = connectionString.replace(/:\/\/([^:]+):([^@]+)@/, '://$1:*****@')
|
||||||
|
console.log('[db] Usando connectionString:', masked)
|
||||||
|
connectionStringLogged = true
|
||||||
|
}
|
||||||
|
|
||||||
pool = new Pool({
|
pool = new Pool({
|
||||||
connectionString,
|
connectionString,
|
||||||
max: 10,
|
max: 10,
|
||||||
@@ -58,7 +65,9 @@ export async function query<T = any>(
|
|||||||
): Promise<pg.QueryResult<T>> {
|
): Promise<pg.QueryResult<T>> {
|
||||||
const pool = getPool()
|
const pool = getPool()
|
||||||
const start = Date.now()
|
const start = Date.now()
|
||||||
|
const maxRetries = 2
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||||
try {
|
try {
|
||||||
const result = await pool.query<T>(text, params)
|
const result = await pool.query<T>(text, params)
|
||||||
const duration = Date.now() - start
|
const duration = Date.now() - start
|
||||||
@@ -68,11 +77,28 @@ export async function query<T = any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('Error ejecutando query:', { text, params, error })
|
const isAuthError = error?.code === '28P01'
|
||||||
|
const isConnError = ['ECONNREFUSED', 'ECONNRESET', '57P01'].includes(error?.code)
|
||||||
|
const shouldRetry = attempt < maxRetries && (isAuthError || isConnError)
|
||||||
|
|
||||||
|
console.error('Error ejecutando query:', { text, params, error: error?.message, code: error?.code, attempt })
|
||||||
|
|
||||||
|
if (shouldRetry) {
|
||||||
|
// Pequeño backoff y reintento
|
||||||
|
await new Promise((res) => setTimeout(res, 500 * (attempt + 1)))
|
||||||
|
// Resetear pool en auth/conn error por si la contraseña/estado cambia en caliente
|
||||||
|
try {
|
||||||
|
await pool.end()
|
||||||
|
} catch (_) {}
|
||||||
|
pool = null
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtiene un cliente del pool para ejecutar transacciones.
|
* Obtiene un cliente del pool para ejecutar transacciones.
|
||||||
|
|||||||
Reference in New Issue
Block a user