Merge pull request #8 from josedario87/feat/shared-prisma-schema
Refactor: Centralize Prisma schema and restrict DB access
This commit is contained in:
224
README.md
224
README.md
@@ -12,125 +12,219 @@
|
|||||||
|
|
||||||
## 📂 Estructura del proyecto
|
## 📂 Estructura del proyecto
|
||||||
|
|
||||||
|
El proyecto ahora está organizado como un monorepo utilizando workspaces (npm/yarn/pnpm).
|
||||||
|
|
||||||
```
|
```
|
||||||
planilla/
|
planilla/
|
||||||
├─ .gitea/workflows/build.yml # CI/CD: build + push + deploy
|
├─ .gitea/workflows/build.yml # CI/CD: build + push + deploy
|
||||||
├─ api/ # servicio API
|
├─ agent/ # Servicio Agent (Node.js)
|
||||||
│ ├─ Dockerfile
|
│ ├─ Dockerfile
|
||||||
|
│ └─ package.json
|
||||||
|
├─ api/ # Servicio API (Node.js + Express)
|
||||||
|
│ ├─ Dockerfile
|
||||||
|
│ ├─ jsconfig.json
|
||||||
│ ├─ package.json
|
│ ├─ package.json
|
||||||
│ └─ server.js
|
│ └─ server.js
|
||||||
├─ ui/ # frontend Vue 3
|
├─ core/
|
||||||
|
│ └─ prisma/ # Paquete compartido para el schema y cliente Prisma
|
||||||
|
│ ├─ package.json
|
||||||
|
│ ├─ tsconfig.json
|
||||||
|
│ ├─ schema.prisma
|
||||||
|
│ ├─ index.ts # Exporta tipos de Prisma
|
||||||
|
│ └─ client.ts # Exporta PrismaClient
|
||||||
|
├─ mcp/ # Servicio MCP (Node.js)
|
||||||
│ ├─ Dockerfile
|
│ ├─ Dockerfile
|
||||||
|
│ └─ package.json
|
||||||
|
├─ ui/ # Frontend Vue 3 + Vite
|
||||||
|
│ ├─ Dockerfile
|
||||||
|
│ ├─ tsconfig.json
|
||||||
|
│ ├─ package.json
|
||||||
│ ├─ index.html
|
│ ├─ index.html
|
||||||
│ ├─ src/
|
│ ├─ src/
|
||||||
│ └─ vite.config.js
|
│ └─ vite.config.js
|
||||||
├─ Dockerfile # imagen raíz (si aplica)
|
├─ worker/ # Servicio Worker (Node.js)
|
||||||
├─ docker-compose.yml # orquestación de todos los servicios
|
│ ├─ Dockerfile
|
||||||
└─ README.md # este documento
|
│ ├─ jsconfig.json
|
||||||
|
│ └─ package.json
|
||||||
|
├─ package.json # package.json raíz para workspaces
|
||||||
|
├─ tsconfig.base.json # Configuración base de TypeScript para todo el monorepo
|
||||||
|
├─ docker-compose.yml # Orquestación de todos los servicios
|
||||||
|
└─ README.md # Este documento
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Workspaces and Shared Packages
|
||||||
|
|
||||||
|
Este monorepo utiliza workspaces para gestionar múltiples paquetes/servicios. La configuración de workspaces se encuentra en el `package.json` raíz.
|
||||||
|
|
||||||
|
### `@empresa/prisma-schema` (`core/prisma`)
|
||||||
|
|
||||||
|
Este es un paquete compartido que centraliza la definición del schema de Prisma y la configuración del cliente.
|
||||||
|
|
||||||
|
* **Schema**: `core/prisma/schema.prisma`
|
||||||
|
* **Generación del Cliente**: El cliente Prisma se genera dentro de este paquete.
|
||||||
|
* **Consumo de Tipos**: Otros servicios (UI, workers, etc.) deben importar los tipos generados por Prisma desde `@empresa/prisma-schema`.
|
||||||
|
```typescript
|
||||||
|
import type { Employee, OtherModel } from '@empresa/prisma-schema';
|
||||||
|
```
|
||||||
|
* **Consumo del Cliente Prisma (`PrismaClient`)**: La instancia de `PrismaClient` solo debe ser utilizada por el servicio `api`. Se importa de la siguiente manera:
|
||||||
|
```javascript
|
||||||
|
// En api/server.js o similar
|
||||||
|
import { PrismaClient } from '@empresa/prisma-schema/client';
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
```
|
||||||
|
Otros servicios **no deben** importar ni instanciar `PrismaClient` directamente. Deben interactuar con la base de datos a través de la API.
|
||||||
|
|
||||||
|
### TypeScript Configuration
|
||||||
|
|
||||||
|
* Un `tsconfig.base.json` en la raíz define configuraciones comunes de TypeScript, incluyendo alias de path para `@empresa/prisma-schema`.
|
||||||
|
* Cada servicio/paquete que utiliza TypeScript tiene su propio `tsconfig.json` (o `jsconfig.json` para proyectos JavaScript) que extiende la configuración base.
|
||||||
|
* El paquete `core/prisma` compila su salida (cliente Prisma y tipos) a su directorio `dist/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📝 Requisitos
|
## 📝 Requisitos
|
||||||
|
|
||||||
* **Docker** (v20+)
|
* **Docker** (v20+)
|
||||||
* **Docker Compose** (v2+)
|
* **Docker Compose** (v2+)
|
||||||
* **Node.js** (v18+) y **npm** para desarrollo local
|
* **Node.js** (v18+) y **npm** (o yarn/pnpm) para desarrollo local y gestión de workspaces.
|
||||||
* **Acceso a red** `app-net` y `principal` en Docker
|
* **Acceso a red** `app-net` y `principal` en Docker
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Variables de entorno
|
## ⚙️ Variables de entorno
|
||||||
|
|
||||||
Si querés cambiar credenciales, editá directamente en `docker-compose.yml` o usá un `.env`:
|
Las variables de entorno relevantes para cada servicio se pueden encontrar en sus respectivos Dockerfiles o en `docker-compose.yml`. La variable `DATABASE_URL` para la conexión a PostgreSQL es utilizada por el paquete `core/prisma` (y por ende, por la `api` que lo consume) y debe estar configurada para que la `api` funcione correctamente.
|
||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
|
# Ejemplo de variables en .env o docker-compose.yml
|
||||||
COMPOSE_PROJECT_NAME=planilla
|
COMPOSE_PROJECT_NAME=planilla
|
||||||
POSTGRES_USER=usuario
|
POSTGRES_USER=usuario
|
||||||
POSTGRES_PASSWORD=clave
|
POSTGRES_PASSWORD=clave
|
||||||
POSTGRES_DB=midb
|
POSTGRES_DB=midb
|
||||||
|
DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Levantando los servicios
|
## 🚀 Levantando los servicios
|
||||||
|
|
||||||
1. **Clonar repo**
|
1. **Clonar repo**
|
||||||
|
```bash
|
||||||
```bash
|
git clone [https://gitea.interno.com/nucleo000/planilla.git](https://gitea.interno.com/nucleo000/planilla.git)
|
||||||
```
|
cd planilla
|
||||||
|
```
|
||||||
git clone [https://gitea.interno.com/nucleo000/planilla.git](https://gitea.interno.com/nucleo000/planilla.git)
|
2. **Instalar dependencias de workspaces** (desde la raíz del monorepo)
|
||||||
cd planilla
|
```bash
|
||||||
|
npm install # o yarn install / pnpm install
|
||||||
````
|
```
|
||||||
2. **Construir y levantar**
|
3. **Generar cliente Prisma** (necesario después de instalar o si cambias el schema)
|
||||||
```bash
|
Desde la raíz del monorepo:
|
||||||
docker compose up -d --build
|
```bash
|
||||||
````
|
npm run db:generate --workspace=@empresa/prisma-schema # o yarn workspace @empresa/prisma-schema db:generate
|
||||||
|
```
|
||||||
3. **Ver logs**
|
O directamente desde el paquete:
|
||||||
|
```bash
|
||||||
```bash
|
cd core/prisma
|
||||||
```
|
npm run db:generate
|
||||||
|
cd ../..
|
||||||
docker compose logs -f api ui
|
```
|
||||||
|
4. **Construir y levantar contenedores Docker**
|
||||||
````
|
```bash
|
||||||
4. **Detener todo**
|
docker compose up -d --build
|
||||||
```bash
|
```
|
||||||
docker compose down --remove-orphans
|
5. **Ver logs**
|
||||||
````
|
```bash
|
||||||
|
docker compose logs -f api ui worker # Agrega otros servicios según sea necesario
|
||||||
|
```
|
||||||
|
6. **Detener todo**
|
||||||
|
```bash
|
||||||
|
docker compose down --remove-orphans
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📡 Acceso a la aplicación
|
## 📡 Acceso a la aplicación
|
||||||
|
|
||||||
* La **UI** no expone puertos en el host. En Nginx Proxy Manager (red `principal`):
|
* La **UI** (`ui` service) no expone puertos en el host. En Nginx Proxy Manager (red `principal`):
|
||||||
|
* **Domino**: `planilla.midominio.com` (o el que configures)
|
||||||
|
* **Scheme**: http
|
||||||
|
* **Forward Hostname**: `planilla-ui` (o el nombre de contenedor que uses en `docker-compose.yml`)
|
||||||
|
* **Forward Port**: `80` (o el puerto que exponga el contenedor de la UI, si es diferente)
|
||||||
|
Posteriormente, puedes habilitar SSL Let’s Encrypt desde la pestaña **SSL** en Nginx Proxy Manager.
|
||||||
|
|
||||||
* **Domino**: `planilla.midominio.com`
|
* La **API** (`api` service) corre internamente, por defecto en el puerto `4000`, y no se expone directamente al exterior. La UI u otros servicios internos pueden accederla por su nombre de servicio y puerto (e.g., `http://api:4000`).
|
||||||
* **Scheme**: http
|
|
||||||
* **Forward Hostname**: `planilla-ui` (o `ui` si así lo nombraste)
|
|
||||||
* **Forward Port**: `80`
|
|
||||||
|
|
||||||
Después podés habilitar SSL Let’s Encrypt desde la pestaña **SSL**.
|
|
||||||
|
|
||||||
* La **API** corre internamente en `planilla-api:4000` y no se expone externamente. Vu hace proxy la UI o clientes internos.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗄️ Detalles de cada servicio
|
## 🗄️ Detalles de cada servicio
|
||||||
|
|
||||||
### Base de datos (db)
|
### Base de datos (`db`)
|
||||||
|
|
||||||
* Imagen: `postgres:15`
|
* Imagen: `postgres:15`
|
||||||
* Volumen persistente: `db_data`
|
* Volumen persistente: `db_data`
|
||||||
* Credenciales en `docker-compose.yml`.
|
* Credenciales y configuración en `docker-compose.yml` y/o archivo `.env`.
|
||||||
|
* El schema es gestionado por Prisma en `core/prisma/schema.prisma`. Las migraciones se aplican a través de los comandos de Prisma CLI.
|
||||||
|
|
||||||
### API (api)
|
### API (`api`)
|
||||||
|
|
||||||
* **Framework**: Express
|
* **Framework**: Express
|
||||||
* **DB**: `pg` (Pool)
|
* Utiliza el cliente Prisma desde `@empresa/prisma-schema/client` para interactuar con la base de datos.
|
||||||
* **Endpoints**:
|
* Las definiciones de tipos (ej. para cuerpos de request/response) pueden ser importadas desde `@empresa/prisma-schema`.
|
||||||
|
* Arranca en el puerto **4000** internamente (configurable).
|
||||||
|
* Código principal en `api/server.js`.
|
||||||
|
|
||||||
* `GET /api/items` → devuelve `items` desde Postgres.
|
### UI (`ui`)
|
||||||
* Arranca en puerto **4000** internamente.
|
|
||||||
* Código principal en `api/server.js`.
|
|
||||||
|
|
||||||
> Aviso: si ves `SyntaxError` al usar `import`, asegurate de tener en `api/package.json`:
|
* **Framework**: Vue 3 + Vite
|
||||||
>
|
* Importa tipos de datos (ej. `Employee`) desde `@empresa/prisma-schema` para type-safety en el frontend.
|
||||||
> ```json
|
* Consume datos de la API (`api` service).
|
||||||
> {
|
* Arranca en el puerto **80** internamente (configurable a través de `vite.config.js` y `Dockerfile`).
|
||||||
> "type": "module"
|
* Código fuente en `ui/src/`.
|
||||||
> }
|
|
||||||
> ```
|
|
||||||
|
|
||||||
### UI (ui)
|
### Worker (`worker`)
|
||||||
|
|
||||||
* **Framework**: Vue 3 + Vite
|
* Servicio Node.js para tareas en segundo plano o programadas.
|
||||||
* **Build**: produce carpeta `dist/` y se sirve con Nginx
|
* Puede importar tipos desde `@empresa/prisma-schema`.
|
||||||
* Arranca en puerto **80** internamente.
|
* Debe interactuar con la base de datos **a través de la API**, no directamente.
|
||||||
* Código fuente en `ui/src/`, configuración en `vite.config.js`.
|
|
||||||
|
### Agent (`agent`) y MCP (`mcp`)
|
||||||
|
|
||||||
|
* Otros servicios Node.js.
|
||||||
|
* Pueden importar tipos desde `@empresa/prisma-schema` si necesitan interactuar con estructuras de datos alineadas con la base de datos.
|
||||||
|
* Deben interactuar con la base de datos **a través de la API**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prisma Migrations
|
||||||
|
|
||||||
|
Las migraciones de la base de datos se gestionan con Prisma CLI desde el paquete `core/prisma`.
|
||||||
|
|
||||||
|
1. **Modificar el schema**: Edita `core/prisma/schema.prisma`.
|
||||||
|
2. **Crear una nueva migración**:
|
||||||
|
Desde la raíz del monorepo:
|
||||||
|
```bash
|
||||||
|
npm run prisma:migrate:dev --workspace=@empresa/prisma-schema -- --name tu-nombre-de-migracion
|
||||||
|
```
|
||||||
|
O directamente desde el paquete `core/prisma`:
|
||||||
|
```bash
|
||||||
|
cd core/prisma
|
||||||
|
npx prisma migrate dev --name tu-nombre-de-migracion
|
||||||
|
cd ../..
|
||||||
|
```
|
||||||
|
Esto generará los archivos SQL de migración en `core/prisma/migrations/`.
|
||||||
|
3. **Aplicar migraciones** (generalmente manejado por el `entrypoint.sh` de la API o un script de despliegue):
|
||||||
|
Desde la raíz:
|
||||||
|
```bash
|
||||||
|
npm run prisma:deploy --workspace=@empresa/prisma-schema
|
||||||
|
```
|
||||||
|
O desde `core/prisma`:
|
||||||
|
```bash
|
||||||
|
npx prisma migrate deploy
|
||||||
|
```
|
||||||
|
4. **Generar cliente Prisma** (después de cambios en el schema o migraciones):
|
||||||
|
Esto se hace con el comando `db:generate` ya mencionado en la sección de levantamiento.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
14
agent/jsconfig.json
Normal file
14
agent/jsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext", // Or appropriate target for your Node.js version
|
||||||
|
"module": "esnext", // Since package.json has "type": "module"
|
||||||
|
// Paths are inherited from tsconfig.base.json
|
||||||
|
"baseUrl": "." // baseUrl is still needed if there are other local paths
|
||||||
|
},
|
||||||
|
"include": ["**/*.js"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"@open-wa/wa-automate": "^4.34.3",
|
"@open-wa/wa-automate": "^4.34.3",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||||
"@philschmid/weather-mcp": "^1.0.0"
|
"@philschmid/weather-mcp": "^1.0.0",
|
||||||
|
"@empresa/prisma-schema": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
api/jsconfig.json
Normal file
14
api/jsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext", // Or appropriate target for your Node.js version
|
||||||
|
"module": "esnext", // Since package.json has "type": "module"
|
||||||
|
// Paths are inherited from tsconfig.base.json
|
||||||
|
"baseUrl": "." // baseUrl is still needed if there are other local paths
|
||||||
|
},
|
||||||
|
"include": ["**/*.js"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -7,13 +7,11 @@
|
|||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.8.2",
|
"@empresa/prisma-schema": "1.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"node-cron": "^4.0.5",
|
"node-cron": "^4.0.5",
|
||||||
"pg": "^8.8.0"
|
"pg": "^8.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {}
|
||||||
"prisma": "^6.8.2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Cliente" (
|
|
||||||
"id" BIGSERIAL NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"cedula" BIGINT NOT NULL,
|
|
||||||
"ubicacion" TEXT NOT NULL DEFAULT '.',
|
|
||||||
"grupo_estudio" TEXT,
|
|
||||||
"empleado" BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
"avatar_url" TEXT,
|
|
||||||
"telefono" TEXT,
|
|
||||||
"idciat" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "Cliente_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Planilla" (
|
|
||||||
"id" BIGSERIAL NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"fecha_desde" TIMESTAMP(3) NOT NULL,
|
|
||||||
"fecha_hasta" TIMESTAMP(3) NOT NULL,
|
|
||||||
"titulo" TEXT NOT NULL,
|
|
||||||
"total" DECIMAL(65,30),
|
|
||||||
"estado" TEXT NOT NULL DEFAULT 'pagado',
|
|
||||||
"fecha_anulado" TIMESTAMP(3),
|
|
||||||
"empleado_id" BIGINT NOT NULL,
|
|
||||||
"creador_id" UUID,
|
|
||||||
"anulador_id" UUID,
|
|
||||||
|
|
||||||
CONSTRAINT "Planilla_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "TareaRealizada" (
|
|
||||||
"id" BIGSERIAL NOT NULL,
|
|
||||||
"empleado_id" BIGINT NOT NULL,
|
|
||||||
"planilla_id" BIGINT,
|
|
||||||
"titulo" TEXT NOT NULL,
|
|
||||||
"precio" DOUBLE PRECISION,
|
|
||||||
"estado" TEXT NOT NULL DEFAULT 'pendiente',
|
|
||||||
"observacion" TEXT,
|
|
||||||
"fecha" TIMESTAMP(3) NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"tipo" TEXT NOT NULL DEFAULT '',
|
|
||||||
"fecha_anulado" TIMESTAMP(3),
|
|
||||||
"creador_id" UUID NOT NULL,
|
|
||||||
"anulador_id" UUID,
|
|
||||||
|
|
||||||
CONSTRAINT "TareaRealizada_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Asistencia" (
|
|
||||||
"id" BIGSERIAL NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"entrada" TIMESTAMP(3) DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
"salida" TIMESTAMP(3),
|
|
||||||
"historial" JSONB,
|
|
||||||
"observacion" TEXT,
|
|
||||||
"estado" TEXT DEFAULT 'pendiente',
|
|
||||||
"fecha_anulado" TIMESTAMP(3),
|
|
||||||
"empleado_id" BIGINT NOT NULL,
|
|
||||||
"creador_id" UUID NOT NULL,
|
|
||||||
"modificado_id" UUID,
|
|
||||||
"anulador_id" UUID,
|
|
||||||
|
|
||||||
CONSTRAINT "Asistencia_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Cliente_cedula_key" ON "Cliente"("cedula");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Planilla" ADD CONSTRAINT "Planilla_empleado_id_fkey" FOREIGN KEY ("empleado_id") REFERENCES "Cliente"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TareaRealizada" ADD CONSTRAINT "TareaRealizada_empleado_id_fkey" FOREIGN KEY ("empleado_id") REFERENCES "Cliente"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Asistencia" ADD CONSTRAINT "Asistencia_empleado_id_fkey" FOREIGN KEY ("empleado_id") REFERENCES "Cliente"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Asistencia" ALTER COLUMN "created_at" SET DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
ALTER COLUMN "updated_at" SET DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
ALTER COLUMN "entrada" SET DEFAULT (now() AT TIME ZONE 'utc');
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Cliente" ALTER COLUMN "created_at" SET DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
ALTER COLUMN "updated_at" SET DEFAULT (now() AT TIME ZONE 'utc');
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Planilla" ALTER COLUMN "created_at" SET DEFAULT (now() AT TIME ZONE 'utc'),
|
|
||||||
ALTER COLUMN "updated_at" SET DEFAULT (now() AT TIME ZONE 'utc');
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "TareaRealizada" ALTER COLUMN "created_at" SET DEFAULT (now() AT TIME ZONE 'utc');
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TareaRealizada" ADD CONSTRAINT "TareaRealizada_planilla_id_fkey" FOREIGN KEY ("planilla_id") REFERENCES "Planilla"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Please do not edit this file manually
|
|
||||||
# It should be added in your version-control system (e.g., Git)
|
|
||||||
provider = "postgresql"
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { PrismaClient } from './prisma/generated/client/index.js';
|
import { PrismaClient } from '@empresa/prisma-schema/client';
|
||||||
import { Decimal } from '@prisma/client/runtime/library.js';
|
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
|
||||||
// Import new routers
|
// Import new routers
|
||||||
@@ -16,7 +15,6 @@ import planillasRouter from './routes/planillas/planillas.js';
|
|||||||
// Resto del código
|
// Resto del código
|
||||||
|
|
||||||
BigInt.prototype.toJSON = function () { return this.toString(); };
|
BigInt.prototype.toJSON = function () { return this.toString(); };
|
||||||
Decimal.prototype.toJSON = function () { return this.toString(); };
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
export const app = express();
|
export const app = express();
|
||||||
|
|||||||
2
core/prisma/client.ts
Normal file
2
core/prisma/client.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// This file exports the PrismaClient instance for use in Node.js environments (specifically the API).
|
||||||
|
export { PrismaClient } from './generated/client';
|
||||||
7
core/prisma/index.ts
Normal file
7
core/prisma/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// This file serves as the entry point for the @empresa/prisma-schema package.
|
||||||
|
// It re-exports only the generated types by default.
|
||||||
|
// For PrismaClient instance, import from '@empresa/prisma-schema/client'.
|
||||||
|
|
||||||
|
export * from './generated/client/index-browser'; // Exports types for browser/non-Node.js environments
|
||||||
|
// If you need all types including Node.js specific ones (less common for shared types):
|
||||||
|
// export * from './generated/client';
|
||||||
27
core/prisma/package.json
Normal file
27
core/prisma/package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "@empresa/prisma-schema",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./client": {
|
||||||
|
"types": "./dist/client.d.ts",
|
||||||
|
"default": "./dist/client.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"db:generate": "prisma generate",
|
||||||
|
"prisma:migrate:dev": "prisma migrate dev",
|
||||||
|
"prisma:deploy": "prisma migrate deploy"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prisma": "^5.10.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^5.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
output = "generated/client"
|
output = "./generated/client"
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
26
core/prisma/tsconfig.json
Normal file
26
core/prisma/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "dist",
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"declarationDir": "dist",
|
||||||
|
// Overriding base to be specific for this package
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {} // Clear paths from base, not needed here
|
||||||
|
},
|
||||||
|
"include": ["./schema.prisma", "./generated/client", "index.ts", "client.ts"],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
".next",
|
||||||
|
".turbo",
|
||||||
|
"coverage",
|
||||||
|
"build",
|
||||||
|
"prisma/seed.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
14
mcp/jsconfig.json
Normal file
14
mcp/jsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext", // Or appropriate target for your Node.js version
|
||||||
|
"module": "esnext", // Since package.json has "type": "module"
|
||||||
|
// Paths are inherited from tsconfig.base.json
|
||||||
|
"baseUrl": "." // baseUrl is still needed if there are other local paths
|
||||||
|
},
|
||||||
|
"include": ["**/*.js"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"start": "node index.js"
|
"start": "node index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.0.0"
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||||
|
"@empresa/prisma-schema": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "empresa-monorepo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"agent",
|
||||||
|
"api",
|
||||||
|
"core/prisma",
|
||||||
|
"mcp",
|
||||||
|
"ui",
|
||||||
|
"worker"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"db:generate": "npm run db:generate --workspace=@empresa/prisma-schema",
|
||||||
|
"prisma:migrate:dev": "npm run prisma:migrate:dev --workspace=@empresa/prisma-schema",
|
||||||
|
"prisma:deploy": "npm run prisma:deploy --workspace=@empresa/prisma-schema"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tsconfig.base.json
Normal file
14
tsconfig.base.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@empresa/prisma-schema": ["core/prisma/index.ts"], // Points to the main entry for types
|
||||||
|
"@empresa/prisma-schema/client": ["core/prisma/client.ts"] // Points to the client entry
|
||||||
|
},
|
||||||
|
// Common options
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1",
|
||||||
|
"@empresa/prisma-schema": "1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.2",
|
"@vitejs/plugin-vue": "^5.2.2",
|
||||||
|
|||||||
@@ -43,20 +43,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import type { Employee } from '@empresa/prisma-schema'
|
||||||
// Define the structure of the employee object based on the Prisma schema
|
|
||||||
interface Employee {
|
|
||||||
id: string | number // Changed from BigInt to string | number for easier handling in frontend
|
|
||||||
name: string
|
|
||||||
cedula: number // Changed from BigInt
|
|
||||||
avatar_url?: string
|
|
||||||
telefono?: string
|
|
||||||
ubicacion: string
|
|
||||||
idciat?: string
|
|
||||||
grupo_estudio?: string
|
|
||||||
// created_at and updated_at are usually not displayed directly in a summary card
|
|
||||||
// empleado: boolean // This is implicit as it's an employee card
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
employee: {
|
employee: {
|
||||||
|
|||||||
23
ui/tsconfig.json
Normal file
23
ui/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true, // Vite handles emission
|
||||||
|
// Add Vue-specific options if not in base
|
||||||
|
"allowJs": true, // If you have JS files too
|
||||||
|
// Vite projects often use this for path aliases like @/
|
||||||
|
// The base already has baseUrl: "."
|
||||||
|
// Paths for @empresa/prisma-schema are inherited from tsconfig.base.json
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"] // Local alias for UI project
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }] // Common for Vite projects
|
||||||
|
}
|
||||||
12
ui/tsconfig.node.json
Normal file
12
ui/tsconfig.node.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext",
|
||||||
|
// Specific overrides for Node context if needed
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.js"] // Or .ts if you use TypeScript for Vite config
|
||||||
|
}
|
||||||
14
worker/jsconfig.json
Normal file
14
worker/jsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext", // Or appropriate target for your Node.js version
|
||||||
|
"module": "esnext", // Since package.json has "type": "module"
|
||||||
|
// Paths are inherited from tsconfig.base.json
|
||||||
|
"baseUrl": "." // baseUrl is still needed if there are other local paths
|
||||||
|
},
|
||||||
|
"include": ["**/*.js"],
|
||||||
|
"exclude": ["node_modules", "prisma"] // Excluding worker/prisma as it's not used anymore
|
||||||
|
}
|
||||||
@@ -7,10 +7,9 @@
|
|||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.7.0",
|
"@empresa/prisma-schema": "1.0.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"node-cron": "^4.0.5",
|
"node-cron": "^4.0.5",
|
||||||
"pg": "^8.8.0",
|
"pg": "^8.8.0"
|
||||||
"prisma": "^6.7.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
// This is your Prisma schema file,
|
|
||||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
||||||
|
|
||||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
||||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
output = "../generated/prisma"
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "postgresql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
@@ -17,33 +17,31 @@ const EXTERNAL_DB_NAME = process.env.EXTERNAL_DB_NAME || 'YOUR_EXTERNAL_DB_NAME'
|
|||||||
const EXTERNAL_DB_EMPLEADOS_TABLE = process.env.EXTERNAL_DB_EMPLEADOS_TABLE || 'empleados_externos'; // User must set this.
|
const EXTERNAL_DB_EMPLEADOS_TABLE = process.env.EXTERNAL_DB_EMPLEADOS_TABLE || 'empleados_externos'; // User must set this.
|
||||||
// --- End of External Database Configuration ---
|
// --- End of External Database Configuration ---
|
||||||
|
|
||||||
import { PrismaClient } from '../api/prisma/generated/client';
|
// Import types from the shared Prisma package.
|
||||||
const prisma = new PrismaClient();
|
// We are not importing PrismaClient here as the worker will fetch data from the API.
|
||||||
|
import type { Cliente } from '@empresa/prisma-schema'; // Assuming Cliente is the relevant type for employees
|
||||||
|
|
||||||
|
// Define a type for the employee data we expect from the API.
|
||||||
|
// This might be identical to Cliente or a subset, depending on the API endpoint.
|
||||||
|
type EmployeeDataFromAPI = Pick<Cliente, 'id' | 'name' | 'cedula' | 'ubicacion' | 'telefono'>;
|
||||||
|
|
||||||
async function syncEmpleadosToExternalDB() {
|
async function syncEmpleadosToExternalDB() {
|
||||||
console.log('[SyncEmpleados] Starting synchronization process...');
|
console.log('[SyncEmpleados] Starting synchronization process...');
|
||||||
// Core logic for synchronization, wrapped in try/catch/finally to ensure
|
|
||||||
// resources like the Prisma client are properly managed (e.g., disconnected).
|
|
||||||
try {
|
try {
|
||||||
// Fetch all 'Cliente' records from the local Prisma database that are marked as 'empleado'.
|
// TODO: Fetch employee data from the API (e.g., GET /api/empleados)
|
||||||
const localEmpleados = await prisma.cliente.findMany({
|
// This is a placeholder for the API call logic.
|
||||||
where: { empleado: true }, // Filters for clients who are also employees
|
console.log('[SyncEmpleados] Fetching employee data from API...');
|
||||||
select: {
|
const response = await fetch('http://localhost:4000/api/empleados'); // Replace with your actual API endpoint
|
||||||
id: true, // Local ID, might be useful for logging/tracing
|
if (!response.ok) {
|
||||||
name: true,
|
throw new Error(`API request failed with status ${response.status}`);
|
||||||
cedula: true,
|
}
|
||||||
ubicacion: true,
|
const localEmpleados: EmployeeDataFromAPI[] = await response.json();
|
||||||
telefono: true,
|
|
||||||
// avatar_url: true, // Add other relevant fields
|
|
||||||
// idciat: true, // Add other relevant fields
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!localEmpleados.length) {
|
if (!localEmpleados || !localEmpleados.length) {
|
||||||
console.log('[SyncEmpleados] No employees found in local database. Nothing to sync.');
|
console.log('[SyncEmpleados] No employees found from API. Nothing to sync.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[SyncEmpleados] Found ${localEmpleados.length} employees to sync.`);
|
console.log(`[SyncEmpleados] Found ${localEmpleados.length} employees from API to sync.`);
|
||||||
|
|
||||||
// --- External DB Connection Logic (User to implement based on EXTERNAL_DB_TYPE) ---
|
// --- External DB Connection Logic (User to implement based on EXTERNAL_DB_TYPE) ---
|
||||||
// User must implement the actual database connection logic here based on EXTERNAL_DB_TYPE
|
// User must implement the actual database connection logic here based on EXTERNAL_DB_TYPE
|
||||||
@@ -119,9 +117,7 @@ async function syncEmpleadosToExternalDB() {
|
|||||||
// Catch any errors that occur during the synchronization process.
|
// Catch any errors that occur during the synchronization process.
|
||||||
console.error('[SyncEmpleados] Error during synchronization:', error);
|
console.error('[SyncEmpleados] Error during synchronization:', error);
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure the Prisma client is always disconnected, even if an error occurs.
|
// No Prisma client to disconnect as we are using API.
|
||||||
await prisma.$disconnect();
|
|
||||||
console.log('[SyncEmpleados] Prisma client disconnected.');
|
|
||||||
}
|
}
|
||||||
console.log('[SyncEmpleados] Synchronization process finished.');
|
console.log('[SyncEmpleados] Synchronization process finished.');
|
||||||
}
|
}
|
||||||
@@ -136,19 +132,17 @@ export { syncEmpleadosToExternalDB };
|
|||||||
// 2. **Manual Testing:**
|
// 2. **Manual Testing:**
|
||||||
// * Once configured, you can test the script by running it directly: `node worker/sync-empleados.js`
|
// * Once configured, you can test the script by running it directly: `node worker/sync-empleados.js`
|
||||||
// * Observe the console logs for any errors or successful completion messages.
|
// * Observe the console logs for any errors or successful completion messages.
|
||||||
// * Verify that data from your local 'Cliente' table (where empleado=true) appears correctly
|
// * Verify that data fetched from the API appears correctly (or is logged as intended for now)
|
||||||
// in your designated external database table (`EXTERNAL_DB_EMPLEADOS_TABLE`).
|
// if you have implemented the external DB sync part.
|
||||||
// * Check that subsequent runs correctly update existing records and insert new ones.
|
// * The cron job in `worker/cron-worker.js` is scheduled to run this script.
|
||||||
// * The cron job in `worker/cron-worker.js` is scheduled to run this script daily at midnight.
|
|
||||||
// You can monitor logs after this time to see its execution.
|
// You can monitor logs after this time to see its execution.
|
||||||
//
|
//
|
||||||
// 3. **Automated Testing (Recommendations for future development):**
|
// 3. **Automated Testing (Recommendations for future development):**
|
||||||
// * **Unit Tests:**
|
// * **Unit Tests:**
|
||||||
// - Mock the Prisma client (`../api/prisma/generated/client`) to return predefined employee data
|
// - Mock the API call (`fetch`) to return predefined employee data.
|
||||||
// and spy on its methods.
|
|
||||||
// - Mock the external database client (e.g., `pg`, `mysql2`) to simulate connection,
|
// - Mock the external database client (e.g., `pg`, `mysql2`) to simulate connection,
|
||||||
// query execution (select, insert, update), and disconnections. This allows testing
|
// query execution (select, insert, update), and disconnections. This allows testing
|
||||||
// the sync logic without actual database dependencies.
|
// the sync logic without actual API or database dependencies.
|
||||||
// * **Integration Tests:**
|
// * **Integration Tests:**
|
||||||
// - If possible, set up a dedicated test instance of your external database.
|
// - If possible, set up a dedicated test instance of your external database.
|
||||||
// - Write tests that populate the local Prisma test database with sample employee data,
|
// - Write tests that populate the local Prisma test database with sample employee data,
|
||||||
|
|||||||
Reference in New Issue
Block a user