agregada funcionalidad realtime postgress, api y ui.
This commit is contained in:
7
Makefile
7
Makefile
@@ -50,3 +50,10 @@ mcp:
|
||||
|
||||
api:
|
||||
cd api && npm install && npm run dev
|
||||
|
||||
# Creates a new Prisma migration in development mode.
|
||||
# Pass the migration name as an argument, e.g.:
|
||||
# make prisma-migrate-dev name=my-migration-name
|
||||
# If no name is provided, it defaults to "new_migration".
|
||||
prisma-migrate-dev:
|
||||
cd api && npx prisma migrate deploy
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
-- Create function and trigger to notify SSE on planilla changes
|
||||
CREATE OR REPLACE FUNCTION notify_planilla_change() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
payload TEXT;
|
||||
BEGIN
|
||||
payload := json_build_object(
|
||||
'table', TG_TABLE_NAME,
|
||||
'operation', TG_OP,
|
||||
'old', row_to_json(OLD),
|
||||
'new', row_to_json(NEW)
|
||||
)::text;
|
||||
PERFORM pg_notify('sse_events', payload);
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
RETURN OLD;
|
||||
ELSE
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS planilla_notify_trigger ON "Planilla";
|
||||
CREATE TRIGGER planilla_notify_trigger
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "Planilla"
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_planilla_change();
|
||||
@@ -1,6 +1,10 @@
|
||||
-- Triggers for other tables to notify SSE channel
|
||||
-- prisma/migrations/20250609211800_notify_others_sse/migration.sql
|
||||
/* 0️⃣ Limpieza */
|
||||
DROP FUNCTION IF EXISTS notify_planilla_change() CASCADE;
|
||||
DROP FUNCTION IF EXISTS notify_generic_change() CASCADE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION notify_generic_change()
|
||||
/* 1️⃣ Función genérica */
|
||||
CREATE OR REPLACE FUNCTION notify_sse_change()
|
||||
RETURNS trigger AS $$
|
||||
DECLARE
|
||||
payload TEXT;
|
||||
@@ -11,7 +15,9 @@ BEGIN
|
||||
'old', row_to_json(OLD),
|
||||
'new', row_to_json(NEW)
|
||||
)::text;
|
||||
|
||||
PERFORM pg_notify('sse_events', payload);
|
||||
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
RETURN OLD;
|
||||
ELSE
|
||||
@@ -20,23 +26,33 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
/* 2️⃣ Drop de triggers viejos (normal o constraint) */
|
||||
DROP TRIGGER IF EXISTS planilla_notify_trigger ON "Planilla";
|
||||
DROP TRIGGER IF EXISTS cliente_notify_trigger ON "Cliente";
|
||||
CREATE TRIGGER cliente_notify_trigger
|
||||
DROP TRIGGER IF EXISTS tarea_notify_trigger ON "TareaRealizada";
|
||||
DROP TRIGGER IF EXISTS asistencia_notify_trigger ON "Asistencia";
|
||||
|
||||
/* 3️⃣ Constraint triggers DEFERRABLE */
|
||||
CREATE CONSTRAINT TRIGGER planilla_notify_trigger
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "Planilla"
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_sse_change();
|
||||
|
||||
CREATE CONSTRAINT TRIGGER cliente_notify_trigger
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "Cliente"
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_generic_change();
|
||||
EXECUTE PROCEDURE notify_sse_change();
|
||||
|
||||
DROP TRIGGER IF EXISTS tarea_notify_trigger ON "TareaRealizada";
|
||||
CREATE TRIGGER tarea_notify_trigger
|
||||
CREATE CONSTRAINT TRIGGER tarea_notify_trigger
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "TareaRealizada"
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_generic_change();
|
||||
EXECUTE PROCEDURE notify_sse_change();
|
||||
|
||||
DROP TRIGGER IF EXISTS asistencia_notify_trigger ON "Asistencia";
|
||||
CREATE TRIGGER asistencia_notify_trigger
|
||||
CREATE CONSTRAINT TRIGGER asistencia_notify_trigger
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "Asistencia"
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_generic_change();
|
||||
EXECUTE PROCEDURE notify_sse_change();
|
||||
|
||||
@@ -31,7 +31,8 @@ services:
|
||||
container_name: planilla-ui
|
||||
image: gitea.interno.com/nucleo000/planilla-ui:latest
|
||||
build: ./ui
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
VITE_API_EVENTS_URL: http://planilla-api:4000/events
|
||||
ports:
|
||||
- "3008:80"
|
||||
networks: [planilla, principal]
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
# planilla/ui/Dockerfile
|
||||
# Etapa de build de Vite
|
||||
FROM node:18-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Etapa final con Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copia la app compilada
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
# Copia script que genera config.js en base a variables de entorno
|
||||
COPY runtime-env.sh /docker-entrypoint.d/
|
||||
|
||||
# Copia configuración de Nginx para asegurar que sirva correctamente la app
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
4
ui/runtime-env.sh
Normal file
4
ui/runtime-env.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
echo "window.RUNTIME_CONFIG = {" > /usr/share/nginx/html/config.js
|
||||
echo " VITE_API_EVENTS_URL: '${VITE_API_EVENTS_URL}'" >> /usr/share/nginx/html/config.js
|
||||
echo "};" >> /usr/share/nginx/html/config.js
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
// forzar subida
|
||||
|
||||
const baseURL = 'https://planilla.interno.com'
|
||||
const baseURL = 'http://localhost:4000';
|
||||
// const baseURL = import.meta.env.API_BASE_URL || 'https://planilla.interno.com'
|
||||
console.log(baseURL);
|
||||
|
||||
|
||||
@@ -76,11 +76,30 @@ export const useEmpleadosStore = defineStore('empleados', {
|
||||
await this.fetchEmpleados();
|
||||
} catch (error) {
|
||||
console.error(`Error eliminando empleado ${id}:`, error);
|
||||
|
||||
const prismaCode = error?.response?.data?.code;
|
||||
const constraint = error?.response?.data?.meta?.constraint;
|
||||
|
||||
if (prismaCode === 'P2003') {
|
||||
if (constraint === 'Planilla_empleado_id_fkey') {
|
||||
throw 'No se puede eliminar: el empleado tiene planillas asociadas.';
|
||||
}
|
||||
if (constraint === 'Asistencia_empleado_id_fkey') {
|
||||
throw 'No se puede eliminar: el empleado tiene asistencias registradas.';
|
||||
}
|
||||
if (constraint === 'TareaRealizada_empleado_id_fkey') {
|
||||
throw 'No se puede eliminar: el empleado tiene tareas registradas.';
|
||||
}
|
||||
// Si no sabemos qué constraint es
|
||||
throw 'No se puede eliminar: el empleado está referenciado en otros datos.';
|
||||
}
|
||||
|
||||
const errorMsg = error.response?.data?.message || error.message || 'Ocurrió un error.';
|
||||
throw errorMsg;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
clearCurrentEmpleado() {
|
||||
this.currentEmpleado = getDefaultEmpleado();
|
||||
},
|
||||
|
||||
@@ -10,32 +10,40 @@ export const useRealtimeStore = defineStore('realtime', {
|
||||
}),
|
||||
actions: {
|
||||
init() {
|
||||
if (this._sse) return
|
||||
this._sse = new EventSource('/events')
|
||||
if (this._sse) return;
|
||||
|
||||
const eventosURL = import.meta.env.VITE_API_EVENTS_URL || '/events';
|
||||
this._sse = new EventSource(eventosURL);
|
||||
|
||||
this._sse.onopen = () => {
|
||||
console.log('🟢 Conexión SSE establecida correctamente');
|
||||
};
|
||||
|
||||
this._sse.onmessage = (e) => {
|
||||
try {
|
||||
const payload = JSON.parse(e.data)
|
||||
const payload = JSON.parse(e.data);
|
||||
switch (payload.table) {
|
||||
case 'Planilla':
|
||||
usePlanillasStore().fetchPlanillas()
|
||||
break
|
||||
usePlanillasStore().fetchPlanillas();
|
||||
break;
|
||||
case 'Cliente':
|
||||
useEmpleadosStore().fetchEmpleados()
|
||||
break
|
||||
useEmpleadosStore().fetchEmpleados();
|
||||
break;
|
||||
case 'TareaRealizada':
|
||||
useTareasStore().fetchTareas()
|
||||
break
|
||||
useTareasStore().fetchTareas();
|
||||
break;
|
||||
case 'Asistencia':
|
||||
useAsistenciasStore().fetchAsistencias()
|
||||
break
|
||||
useAsistenciasStore().fetchAsistencias();
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error procesando SSE', err)
|
||||
}
|
||||
console.error('Error procesando SSE', err);
|
||||
}
|
||||
};
|
||||
|
||||
this._sse.onerror = () => {
|
||||
console.warn('SSE connection lost, reloading...')
|
||||
console.warn('SSE connection lost, reloading...');
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user