Initial JARED implementation - Predefined flows manager for CORP
- Flask API with full CRUD for flows - Execute flow with OK->FELDMAN / incidencia->MASON routing - PostgreSQL integration with host DB - Docker deployment on port 5052 - S-CONTRACT v2.1 compliant
This commit is contained in:
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
H_INSTANCIA=your_h_instancia_here
|
||||||
|
DB_HOST=172.17.0.1
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=corp
|
||||||
|
DB_USER=corp
|
||||||
|
DB_PASSWORD=your_password_here
|
||||||
|
PORT=5052
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY app.py .
|
||||||
|
|
||||||
|
EXPOSE 5052
|
||||||
|
|
||||||
|
CMD ["gunicorn", "--bind", "0.0.0.0:5052", "--workers", "2", "app:app"]
|
||||||
57
README.md
Normal file
57
README.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# JARED - Predefined Flows Manager (CORP)
|
||||||
|
|
||||||
|
Microservicio para gestionar flujos de trabajo predefinidos en el servidor CORP del ecosistema TZZR.
|
||||||
|
|
||||||
|
## Descripcion
|
||||||
|
|
||||||
|
JARED maneja la creacion, listado y ejecucion de flujos predefinidos. Cada ejecucion se enruta a:
|
||||||
|
- **FELDMAN** si el flujo termina OK
|
||||||
|
- **MASON** si hay incidencia
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Metodo | Auth | Descripcion |
|
||||||
|
|----------|--------|------|-------------|
|
||||||
|
| `/health` | GET | No | Health check |
|
||||||
|
| `/s-contract` | GET | No | Contrato del servicio |
|
||||||
|
| `/flujos` | GET | Si | Listar flujos |
|
||||||
|
| `/flujos` | POST | Si | Crear flujo |
|
||||||
|
| `/flujos/<id>` | GET | Si | Obtener flujo |
|
||||||
|
| `/flujos/<id>` | PUT | Si | Actualizar flujo |
|
||||||
|
| `/flujos/<id>` | DELETE | Si | Eliminar flujo |
|
||||||
|
| `/ejecutar/<id>` | POST | Si | Ejecutar flujo |
|
||||||
|
| `/ejecuciones` | GET | Si | Listar ejecuciones |
|
||||||
|
| `/stats` | GET | Si | Estadisticas |
|
||||||
|
|
||||||
|
## Autenticacion
|
||||||
|
|
||||||
|
Header `X-Auth-Key` con el h_instancia de CORP.
|
||||||
|
|
||||||
|
## Despliegue
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/jared
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuracion (.env)
|
||||||
|
|
||||||
|
```
|
||||||
|
H_INSTANCIA=<h_instancia_corp>
|
||||||
|
DB_HOST=172.17.0.1
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=corp
|
||||||
|
DB_USER=corp
|
||||||
|
DB_PASSWORD=<password>
|
||||||
|
PORT=5052
|
||||||
|
```
|
||||||
|
|
||||||
|
## Base de Datos
|
||||||
|
|
||||||
|
Tablas en PostgreSQL (corp):
|
||||||
|
- `flujos_predefinidos` - Definiciones de flujos
|
||||||
|
- `flujo_ejecuciones` - Historial de ejecuciones
|
||||||
|
|
||||||
|
## Puerto
|
||||||
|
|
||||||
|
5052
|
||||||
347
app.py
Normal file
347
app.py
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from functools import wraps
|
||||||
|
import psycopg2
|
||||||
|
from psycopg2.extras import RealDictCursor
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
H_INSTANCIA = os.environ.get('H_INSTANCIA')
|
||||||
|
DB_HOST = os.environ.get('DB_HOST', 'localhost')
|
||||||
|
DB_PORT = os.environ.get('DB_PORT', '5432')
|
||||||
|
DB_NAME = os.environ.get('DB_NAME', 'corp')
|
||||||
|
DB_USER = os.environ.get('DB_USER', 'corp')
|
||||||
|
DB_PASSWORD = os.environ.get('DB_PASSWORD', '')
|
||||||
|
PORT = int(os.environ.get('PORT', 5052))
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
return psycopg2.connect(
|
||||||
|
host=DB_HOST,
|
||||||
|
port=DB_PORT,
|
||||||
|
database=DB_NAME,
|
||||||
|
user=DB_USER,
|
||||||
|
password=DB_PASSWORD,
|
||||||
|
cursor_factory=RealDictCursor
|
||||||
|
)
|
||||||
|
|
||||||
|
def require_auth(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
auth_key = request.headers.get('X-Auth-Key')
|
||||||
|
if not auth_key or auth_key != H_INSTANCIA:
|
||||||
|
return jsonify({'error': 'Unauthorized', 'code': 401}), 401
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
def generate_hash(data):
|
||||||
|
return hashlib.sha256(f"{data}{datetime.now().isoformat()}".encode()).hexdigest()
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
@app.route('/health', methods=['GET'])
|
||||||
|
def health():
|
||||||
|
try:
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('SELECT 1')
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({
|
||||||
|
'status': 'healthy',
|
||||||
|
'service': 'jared',
|
||||||
|
'version': '1.0.0',
|
||||||
|
'timestamp': datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'unhealthy',
|
||||||
|
'error': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
# S-CONTRACT endpoint
|
||||||
|
@app.route('/s-contract', methods=['GET'])
|
||||||
|
def s_contract():
|
||||||
|
return jsonify({
|
||||||
|
'service': 'jared',
|
||||||
|
'version': '1.0.0',
|
||||||
|
'contract_version': 'S-CONTRACT v2.1',
|
||||||
|
'endpoints': {
|
||||||
|
'/health': {'method': 'GET', 'auth': False, 'desc': 'Health check'},
|
||||||
|
'/flujos': {'method': 'GET', 'auth': True, 'desc': 'List predefined flows'},
|
||||||
|
'/flujos': {'method': 'POST', 'auth': True, 'desc': 'Create flow'},
|
||||||
|
'/flujos/<id>': {'method': 'GET', 'auth': True, 'desc': 'Get flow'},
|
||||||
|
'/flujos/<id>': {'method': 'PUT', 'auth': True, 'desc': 'Update flow'},
|
||||||
|
'/flujos/<id>': {'method': 'DELETE', 'auth': True, 'desc': 'Delete flow'},
|
||||||
|
'/ejecutar/<id>': {'method': 'POST', 'auth': True, 'desc': 'Execute flow'},
|
||||||
|
'/ejecuciones': {'method': 'GET', 'auth': True, 'desc': 'List executions'},
|
||||||
|
'/stats': {'method': 'GET', 'auth': True, 'desc': 'Statistics'}
|
||||||
|
},
|
||||||
|
'auth': 'X-Auth-Key header with h_instancia'
|
||||||
|
})
|
||||||
|
|
||||||
|
# List flows
|
||||||
|
@app.route('/flujos', methods=['GET'])
|
||||||
|
@require_auth
|
||||||
|
def list_flujos():
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('''
|
||||||
|
SELECT id, nombre, descripcion, pasos, campos_fijos, campos_variables, activo, created_at
|
||||||
|
FROM flujos_predefinidos
|
||||||
|
WHERE h_instancia = %s
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
''', (H_INSTANCIA,))
|
||||||
|
flujos = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'flujos': [dict(f) for f in flujos], 'count': len(flujos)})
|
||||||
|
|
||||||
|
# Create flow
|
||||||
|
@app.route('/flujos', methods=['POST'])
|
||||||
|
@require_auth
|
||||||
|
def create_flujo():
|
||||||
|
data = request.get_json()
|
||||||
|
if not data or 'nombre' not in data or 'pasos' not in data:
|
||||||
|
return jsonify({'error': 'nombre and pasos required'}), 400
|
||||||
|
|
||||||
|
flujo_id = generate_hash(data['nombre'])[:64]
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute('''
|
||||||
|
INSERT INTO flujos_predefinidos
|
||||||
|
(id, h_instancia, nombre, descripcion, pasos, campos_fijos, campos_variables)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||||
|
RETURNING id, nombre, created_at
|
||||||
|
''', (
|
||||||
|
flujo_id,
|
||||||
|
H_INSTANCIA,
|
||||||
|
data['nombre'],
|
||||||
|
data.get('descripcion', ''),
|
||||||
|
json.dumps(data['pasos']),
|
||||||
|
json.dumps(data.get('campos_fijos', {})),
|
||||||
|
json.dumps(data.get('campos_variables', []))
|
||||||
|
))
|
||||||
|
result = cur.fetchone()
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True, 'flujo': dict(result)}), 201
|
||||||
|
except psycopg2.IntegrityError:
|
||||||
|
conn.rollback()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': 'Flow already exists'}), 409
|
||||||
|
|
||||||
|
# Get flow
|
||||||
|
@app.route('/flujos/<flujo_id>', methods=['GET'])
|
||||||
|
@require_auth
|
||||||
|
def get_flujo(flujo_id):
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('''
|
||||||
|
SELECT * FROM flujos_predefinidos
|
||||||
|
WHERE id = %s AND h_instancia = %s
|
||||||
|
''', (flujo_id, H_INSTANCIA))
|
||||||
|
flujo = cur.fetchone()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
if not flujo:
|
||||||
|
return jsonify({'error': 'Flow not found'}), 404
|
||||||
|
return jsonify({'flujo': dict(flujo)})
|
||||||
|
|
||||||
|
# Update flow
|
||||||
|
@app.route('/flujos/<flujo_id>', methods=['PUT'])
|
||||||
|
@require_auth
|
||||||
|
def update_flujo(flujo_id):
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({'error': 'No data provided'}), 400
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
updates = []
|
||||||
|
values = []
|
||||||
|
if 'nombre' in data:
|
||||||
|
updates.append('nombre = %s')
|
||||||
|
values.append(data['nombre'])
|
||||||
|
if 'descripcion' in data:
|
||||||
|
updates.append('descripcion = %s')
|
||||||
|
values.append(data['descripcion'])
|
||||||
|
if 'pasos' in data:
|
||||||
|
updates.append('pasos = %s')
|
||||||
|
values.append(json.dumps(data['pasos']))
|
||||||
|
if 'campos_fijos' in data:
|
||||||
|
updates.append('campos_fijos = %s')
|
||||||
|
values.append(json.dumps(data['campos_fijos']))
|
||||||
|
if 'campos_variables' in data:
|
||||||
|
updates.append('campos_variables = %s')
|
||||||
|
values.append(json.dumps(data['campos_variables']))
|
||||||
|
if 'activo' in data:
|
||||||
|
updates.append('activo = %s')
|
||||||
|
values.append(data['activo'])
|
||||||
|
|
||||||
|
updates.append('updated_at = NOW()')
|
||||||
|
values.extend([flujo_id, H_INSTANCIA])
|
||||||
|
|
||||||
|
cur.execute(f'''
|
||||||
|
UPDATE flujos_predefinidos
|
||||||
|
SET {', '.join(updates)}
|
||||||
|
WHERE id = %s AND h_instancia = %s
|
||||||
|
RETURNING id, nombre, updated_at
|
||||||
|
''', values)
|
||||||
|
result = cur.fetchone()
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return jsonify({'error': 'Flow not found'}), 404
|
||||||
|
return jsonify({'success': True, 'flujo': dict(result)})
|
||||||
|
|
||||||
|
# Delete flow
|
||||||
|
@app.route('/flujos/<flujo_id>', methods=['DELETE'])
|
||||||
|
@require_auth
|
||||||
|
def delete_flujo(flujo_id):
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('''
|
||||||
|
DELETE FROM flujos_predefinidos
|
||||||
|
WHERE id = %s AND h_instancia = %s
|
||||||
|
RETURNING id
|
||||||
|
''', (flujo_id, H_INSTANCIA))
|
||||||
|
result = cur.fetchone()
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
if not result:
|
||||||
|
return jsonify({'error': 'Flow not found'}), 404
|
||||||
|
return jsonify({'success': True, 'deleted': flujo_id})
|
||||||
|
|
||||||
|
# Execute flow
|
||||||
|
@app.route('/ejecutar/<flujo_id>', methods=['POST'])
|
||||||
|
@require_auth
|
||||||
|
def ejecutar_flujo(flujo_id):
|
||||||
|
data = request.get_json() or {}
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Get flow
|
||||||
|
cur.execute('''
|
||||||
|
SELECT * FROM flujos_predefinidos
|
||||||
|
WHERE id = %s AND h_instancia = %s AND activo = true
|
||||||
|
''', (flujo_id, H_INSTANCIA))
|
||||||
|
flujo = cur.fetchone()
|
||||||
|
|
||||||
|
if not flujo:
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': 'Flow not found or inactive'}), 404
|
||||||
|
|
||||||
|
# Determine estado and destino
|
||||||
|
hay_incidencia = data.get('incidencia', False)
|
||||||
|
estado = 'incidencia' if hay_incidencia else 'ok'
|
||||||
|
destino = 'mason' if hay_incidencia else 'feldman'
|
||||||
|
|
||||||
|
h_ejecucion = generate_hash(f"{flujo_id}{json.dumps(data)}")[:64]
|
||||||
|
|
||||||
|
# Merge campos_fijos with provided data
|
||||||
|
datos_completos = {**flujo['campos_fijos'], **data}
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
INSERT INTO flujo_ejecuciones
|
||||||
|
(h_flujo, h_instancia, h_ejecucion, datos, estado, destino, notas)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||||
|
RETURNING id, h_ejecucion, estado, destino, created_at
|
||||||
|
''', (
|
||||||
|
flujo_id,
|
||||||
|
H_INSTANCIA,
|
||||||
|
h_ejecucion,
|
||||||
|
json.dumps(datos_completos),
|
||||||
|
estado,
|
||||||
|
destino,
|
||||||
|
data.get('notas', '')
|
||||||
|
))
|
||||||
|
result = cur.fetchone()
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'ejecucion': dict(result),
|
||||||
|
'flujo_nombre': flujo['nombre'],
|
||||||
|
'routing': {
|
||||||
|
'estado': estado,
|
||||||
|
'destino': destino,
|
||||||
|
'mensaje': f"Enviando a {destino.upper()}" + (" por incidencia" if hay_incidencia else " (OK)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# List executions
|
||||||
|
@app.route('/ejecuciones', methods=['GET'])
|
||||||
|
@require_auth
|
||||||
|
def list_ejecuciones():
|
||||||
|
limit = request.args.get('limit', 50, type=int)
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('''
|
||||||
|
SELECT e.*, f.nombre as flujo_nombre
|
||||||
|
FROM flujo_ejecuciones e
|
||||||
|
LEFT JOIN flujos_predefinidos f ON e.h_flujo = f.id
|
||||||
|
WHERE e.h_instancia = %s
|
||||||
|
ORDER BY e.created_at DESC
|
||||||
|
LIMIT %s
|
||||||
|
''', (H_INSTANCIA, limit))
|
||||||
|
ejecuciones = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'ejecuciones': [dict(e) for e in ejecuciones], 'count': len(ejecuciones)})
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
@app.route('/stats', methods=['GET'])
|
||||||
|
@require_auth
|
||||||
|
def stats():
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
cur.execute('SELECT COUNT(*) as total FROM flujos_predefinidos WHERE h_instancia = %s', (H_INSTANCIA,))
|
||||||
|
total_flujos = cur.fetchone()['total']
|
||||||
|
|
||||||
|
cur.execute('SELECT COUNT(*) as total FROM flujo_ejecuciones WHERE h_instancia = %s', (H_INSTANCIA,))
|
||||||
|
total_ejecuciones = cur.fetchone()['total']
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT estado, COUNT(*) as count
|
||||||
|
FROM flujo_ejecuciones
|
||||||
|
WHERE h_instancia = %s
|
||||||
|
GROUP BY estado
|
||||||
|
''', (H_INSTANCIA,))
|
||||||
|
por_estado = {r['estado']: r['count'] for r in cur.fetchall()}
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT destino, COUNT(*) as count
|
||||||
|
FROM flujo_ejecuciones
|
||||||
|
WHERE h_instancia = %s
|
||||||
|
GROUP BY destino
|
||||||
|
''', (H_INSTANCIA,))
|
||||||
|
por_destino = {r['destino']: r['count'] for r in cur.fetchall()}
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'flujos_totales': total_flujos,
|
||||||
|
'ejecuciones_totales': total_ejecuciones,
|
||||||
|
'por_estado': por_estado,
|
||||||
|
'por_destino': por_destino
|
||||||
|
})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=PORT, debug=False)
|
||||||
25
docker-compose.yml
Normal file
25
docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
jared:
|
||||||
|
build: .
|
||||||
|
container_name: jared-service
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5052:5052"
|
||||||
|
environment:
|
||||||
|
- H_INSTANCIA=${H_INSTANCIA}
|
||||||
|
- DB_HOST=${DB_HOST}
|
||||||
|
- DB_PORT=${DB_PORT}
|
||||||
|
- DB_NAME=${DB_NAME}
|
||||||
|
- DB_USER=${DB_USER}
|
||||||
|
- DB_PASSWORD=${DB_PASSWORD}
|
||||||
|
- PORT=5052
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
networks:
|
||||||
|
- tzzr-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
tzzr-network:
|
||||||
|
external: true
|
||||||
37
init.sql
Normal file
37
init.sql
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-- JARED database tables for CORP
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS flujos_predefinidos (
|
||||||
|
id VARCHAR(64) PRIMARY KEY,
|
||||||
|
h_instancia VARCHAR(64) NOT NULL,
|
||||||
|
nombre VARCHAR(100) NOT NULL,
|
||||||
|
descripcion TEXT,
|
||||||
|
pasos JSONB NOT NULL,
|
||||||
|
campos_fijos JSONB DEFAULT '{}',
|
||||||
|
campos_variables JSONB DEFAULT '[]',
|
||||||
|
activo BOOLEAN DEFAULT true,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS flujo_ejecuciones (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
h_flujo VARCHAR(64) REFERENCES flujos_predefinidos(id),
|
||||||
|
h_instancia VARCHAR(64) NOT NULL,
|
||||||
|
h_ejecucion VARCHAR(64) NOT NULL UNIQUE,
|
||||||
|
datos JSONB NOT NULL,
|
||||||
|
estado VARCHAR(20) DEFAULT 'ok',
|
||||||
|
destino VARCHAR(20) DEFAULT 'feldman',
|
||||||
|
notas TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_flujos_h_instancia ON flujos_predefinidos(h_instancia);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ejecuciones_h_instancia ON flujo_ejecuciones(h_instancia);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ejecuciones_estado ON flujo_ejecuciones(estado);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ejecuciones_destino ON flujo_ejecuciones(destino);
|
||||||
|
|
||||||
|
-- Permissions (run as postgres superuser)
|
||||||
|
-- GRANT ALL PRIVILEGES ON TABLE flujos_predefinidos TO corp;
|
||||||
|
-- GRANT ALL PRIVILEGES ON TABLE flujo_ejecuciones TO corp;
|
||||||
|
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO corp;
|
||||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
flask==3.0.0
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
|
gunicorn==21.2.0
|
||||||
Reference in New Issue
Block a user