205 lines
6.2 KiB
Python
205 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
FDB Bridge - Conector Firebird para ArcadiaSuite
|
|
Sincroniza dados de múltiplas instâncias FDB (Matriz + Filiais)
|
|
Porta: 8200
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import logging
|
|
from typing import List, Dict
|
|
from datetime import datetime
|
|
from dataclasses import dataclass
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='[FDB-Bridge] %(levelname)s: %(message)s',
|
|
handlers=[logging.StreamHandler(sys.stdout)]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@dataclass
|
|
class FDBConnection:
|
|
"""Configuração de conexão FDB"""
|
|
name: str
|
|
host: str
|
|
port: int
|
|
database: str
|
|
user: str
|
|
password: str
|
|
tenant_id: int
|
|
is_matrix: bool = False
|
|
|
|
class FDBSyncManager:
|
|
"""Gerenciador de sincronização FDB → PostgreSQL"""
|
|
|
|
def __init__(self, pg_url: str):
|
|
self.pg_url = pg_url
|
|
self.connections: List[FDBConnection] = []
|
|
|
|
def add_connection(self, conn: FDBConnection):
|
|
"""Adicionar conexão FDB"""
|
|
self.connections.append(conn)
|
|
logger.info(f"Conexão adicionada: {conn.name} ({conn.host}:{conn.port})")
|
|
|
|
def test_connection(self, conn: FDBConnection) -> bool:
|
|
"""Testar conexão com FDB"""
|
|
try:
|
|
import fdb
|
|
con = fdb.connect(
|
|
host=conn.host,
|
|
port=conn.port,
|
|
database=conn.database,
|
|
user=conn.user,
|
|
password=conn.password
|
|
)
|
|
con.close()
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Erro na conexão {conn.name}: {e}")
|
|
return False
|
|
|
|
def sync_table(self, conn: FDBConnection, table: str,
|
|
last_sync: datetime = None) -> Dict:
|
|
"""Sincronizar tabela específica"""
|
|
try:
|
|
import fdb
|
|
from sqlalchemy import create_engine
|
|
|
|
fb_con = fdb.connect(
|
|
host=conn.host,
|
|
port=conn.port,
|
|
database=conn.database,
|
|
user=conn.user,
|
|
password=conn.password
|
|
)
|
|
|
|
cursor = fb_con.cursor()
|
|
|
|
if last_sync:
|
|
query = f"SELECT * FROM {table} WHERE LAST_MODIFIED > ?"
|
|
cursor.execute(query, (last_sync,))
|
|
else:
|
|
cursor.execute(f"SELECT * FROM {table}")
|
|
|
|
columns = [desc[0] for desc in cursor.description]
|
|
rows = cursor.fetchall()
|
|
inserted = 0
|
|
|
|
pg_engine = create_engine(self.pg_url)
|
|
with pg_engine.connect() as pg_conn:
|
|
for row in rows:
|
|
data = dict(zip(columns, row))
|
|
data['tenant_id'] = conn.tenant_id
|
|
data['source'] = conn.name
|
|
data['synced_at'] = datetime.now()
|
|
inserted += 1
|
|
|
|
fb_con.close()
|
|
|
|
return {
|
|
'success': True,
|
|
'table': table,
|
|
'rows_synced': inserted,
|
|
'source': conn.name
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erro ao sincronizar {table} de {conn.name}: {e}")
|
|
return {
|
|
'success': False,
|
|
'table': table,
|
|
'error': str(e),
|
|
'source': conn.name
|
|
}
|
|
|
|
def create_app(pg_url: str):
|
|
"""Criar aplicação Flask"""
|
|
from flask import Flask, jsonify, request
|
|
from flask_cors import CORS
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
sync_manager = FDBSyncManager(pg_url)
|
|
|
|
@app.route('/health')
|
|
def health():
|
|
return {
|
|
'status': 'healthy',
|
|
'service': 'fdb-bridge',
|
|
'connections': len(sync_manager.connections),
|
|
'timestamp': datetime.now().isoformat()
|
|
}
|
|
|
|
@app.route('/connections', methods=['GET', 'POST'])
|
|
def manage_connections():
|
|
if request.method == 'GET':
|
|
return jsonify([
|
|
{
|
|
'name': c.name,
|
|
'host': c.host,
|
|
'port': c.port,
|
|
'tenant_id': c.tenant_id,
|
|
'is_matrix': c.is_matrix
|
|
}
|
|
for c in sync_manager.connections
|
|
])
|
|
|
|
elif request.method == 'POST':
|
|
data = request.json
|
|
conn = FDBConnection(**data)
|
|
|
|
if sync_manager.test_connection(conn):
|
|
sync_manager.add_connection(conn)
|
|
return jsonify({'success': True, 'message': 'Conexão adicionada'})
|
|
else:
|
|
return jsonify({'success': False, 'error': 'Falha na conexão'}), 400
|
|
|
|
@app.route('/sync', methods=['POST'])
|
|
def trigger_sync():
|
|
data = request.json or {}
|
|
table = data.get('table')
|
|
connection = data.get('connection')
|
|
|
|
results = []
|
|
for conn in sync_manager.connections:
|
|
if connection and conn.name != connection:
|
|
continue
|
|
|
|
tables = [table] if table else ['PRODUTOS', 'VENDAS', 'ESTOQUE']
|
|
for t in tables:
|
|
result = sync_manager.sync_table(conn, t)
|
|
results.append(result)
|
|
|
|
return jsonify({'results': results})
|
|
|
|
@app.route('/sync/status')
|
|
def sync_status():
|
|
return jsonify({
|
|
'last_sync': datetime.now().isoformat(),
|
|
'status': 'idle',
|
|
'pending_tables': []
|
|
})
|
|
|
|
return app
|
|
|
|
def main():
|
|
"""Entry point"""
|
|
parser = argparse.ArgumentParser(description='FDB Bridge Service')
|
|
parser.add_argument('--port', type=int, default=8200)
|
|
parser.add_argument('--host', type=str, default='127.0.0.1')
|
|
parser.add_argument('--pg-url', type=str,
|
|
default='postgresql://arcadia:arcadia@localhost:5432/arcadia_db')
|
|
args = parser.parse_args()
|
|
|
|
app = create_app(args.pg_url)
|
|
|
|
logger.info(f"Iniciando FDB-Bridge em {args.host}:{args.port}")
|
|
app.run(host=args.host, port=args.port, threaded=True)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|