Initial FELDMAN implementation - Completed flows registry
- Flask API for storing completed flows - Receives OK flows from ALFRED/JARED - Receives corrected data from MASON - Docker deployment on port 5054
This commit is contained in:
124
app.py
Normal file
124
app.py
Normal file
@@ -0,0 +1,124 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from functools import wraps
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
import os
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
H_INSTANCIA = os.environ.get('H_INSTANCIA')
|
||||
DB_HOST = os.environ.get('DB_HOST', '172.17.0.1')
|
||||
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', 'corp')
|
||||
PORT = int(os.environ.get('PORT', 5054))
|
||||
|
||||
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'}), 401
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
|
||||
def generate_hash(data):
|
||||
return hashlib.sha256(f"{data}{datetime.now().isoformat()}".encode()).hexdigest()[:64]
|
||||
|
||||
@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': 'feldman', 'version': '1.0.0'})
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'unhealthy', 'error': str(e)}), 500
|
||||
|
||||
@app.route('/s-contract', methods=['GET'])
|
||||
def s_contract():
|
||||
return jsonify({
|
||||
'service': 'feldman',
|
||||
'version': '1.0.0',
|
||||
'contract_version': 'S-CONTRACT v2.1',
|
||||
'description': 'Receives routed OK flows from ALFRED/JARED',
|
||||
'endpoints': {
|
||||
'/health': {'method': 'GET', 'auth': False},
|
||||
'/recibir': {'method': 'POST', 'auth': True, 'desc': 'Receive completed flow'},
|
||||
'/completados': {'method': 'GET', 'auth': True, 'desc': 'List completed flows'},
|
||||
'/stats': {'method': 'GET', 'auth': True, 'desc': 'Statistics'}
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/recibir', methods=['POST'])
|
||||
@require_auth
|
||||
def recibir():
|
||||
data = request.get_json() or {}
|
||||
h_completado = generate_hash(str(data))
|
||||
|
||||
conn = get_db()
|
||||
cur = conn.cursor()
|
||||
cur.execute('''
|
||||
INSERT INTO completados
|
||||
(h_completado, h_instancia_origen, h_ejecucion, flujo_nombre, datos, notas)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
RETURNING id, h_completado, created_at
|
||||
''', (
|
||||
h_completado,
|
||||
data.get('h_instancia_origen', 'unknown'),
|
||||
data.get('h_ejecucion', ''),
|
||||
data.get('flujo_nombre', ''),
|
||||
psycopg2.extras.Json(data),
|
||||
data.get('notas', '')
|
||||
))
|
||||
result = cur.fetchone()
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify({'success': True, 'registro': dict(result), 'service': 'feldman'})
|
||||
|
||||
@app.route('/completados', methods=['GET'])
|
||||
@require_auth
|
||||
def completados():
|
||||
limit = request.args.get('limit', 50, type=int)
|
||||
conn = get_db()
|
||||
cur = conn.cursor()
|
||||
cur.execute('SELECT * FROM completados ORDER BY created_at DESC LIMIT %s', (limit,))
|
||||
records = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'completados': [dict(r) for r in records], 'count': len(records)})
|
||||
|
||||
@app.route('/stats', methods=['GET'])
|
||||
@require_auth
|
||||
def stats():
|
||||
conn = get_db()
|
||||
cur = conn.cursor()
|
||||
cur.execute('SELECT COUNT(*) as total FROM completados')
|
||||
total = cur.fetchone()['total']
|
||||
cur.execute('''
|
||||
SELECT DATE(created_at) as fecha, COUNT(*) as count
|
||||
FROM completados
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY fecha DESC LIMIT 7
|
||||
''')
|
||||
por_dia = {str(r['fecha']): r['count'] for r in cur.fetchall()}
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'total': total, 'por_dia': por_dia})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=PORT, debug=False)
|
||||
Reference in New Issue
Block a user