- Unified Dockerfile with thick/thin mode auto-detection - Single docker-compose.yaml with build arguments - Auto-detect logic: thick mode for Oracle 10g/11g, thin mode for 12.1+ - Simplified .env configuration with clear mode selection - Updated admin.py with FORCE_THIN_MODE and INSTANTCLIENTPATH support - Added comprehensive documentation for both deployment modes - Container tested successfully with thick mode for Oracle 11g compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
250 lines
9.5 KiB
Python
250 lines
9.5 KiB
Python
"""
|
|
Flask Admin Interface pentru Import Comenzi Web → ROA
|
|
Gestionează mapările SKU în tabelul ARTICOLE_TERTI
|
|
"""
|
|
|
|
from flask import Flask, jsonify, request, render_template_string
|
|
from flask_cors import CORS
|
|
from dotenv import load_dotenv
|
|
import oracledb
|
|
import os
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
# Configurare environment
|
|
load_dotenv()
|
|
|
|
# Configurare logging
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format='%(asctime)s | %(levelname)s | %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('/app/logs/admin.log'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Environment Variables pentru Oracle
|
|
user = os.environ['ORACLE_USER']
|
|
password = os.environ['ORACLE_PASSWORD']
|
|
dsn = os.environ['ORACLE_DSN']
|
|
|
|
# Oracle client - AUTO-DETECT: thick mode pentru 10g/11g, thin mode pentru 12.1+
|
|
force_thin_mode = os.environ.get('FORCE_THIN_MODE', 'false').lower() == 'true'
|
|
instantclient_path = os.environ.get('INSTANTCLIENTPATH')
|
|
|
|
if force_thin_mode:
|
|
logger.info(f"FORCE_THIN_MODE=true: Folosind thin mode pentru {dsn} (Oracle 12.1+ required)")
|
|
elif instantclient_path:
|
|
try:
|
|
oracledb.init_oracle_client(lib_dir=instantclient_path)
|
|
logger.info(f"Thick mode activat pentru {dsn} (compatibil Oracle 10g/11g/12.1+)")
|
|
except Exception as e:
|
|
logger.error(f"Eroare thick mode: {e}")
|
|
logger.info("Fallback la thin mode - verifică că Oracle DB este 12.1+")
|
|
else:
|
|
logger.info(f"Thin mode (default) pentru {dsn} - Oracle 12.1+ required")
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
def start_pool():
|
|
"""Inițializează connection pool Oracle"""
|
|
try:
|
|
pool = oracledb.create_pool(
|
|
user=user,
|
|
password=password,
|
|
dsn=dsn,
|
|
min=2,
|
|
max=4,
|
|
increment=1
|
|
)
|
|
logger.info(f"Oracle pool creat cu succes pentru {dsn}")
|
|
return pool
|
|
except Exception as e:
|
|
logger.error(f"Eroare creare pool Oracle: {e}")
|
|
raise
|
|
|
|
@app.route('/health')
|
|
def health():
|
|
"""Health check pentru Docker"""
|
|
return jsonify({"status": "ok", "timestamp": datetime.now().isoformat()})
|
|
|
|
@app.route('/')
|
|
def home():
|
|
"""Pagina principală admin interface"""
|
|
html_template = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>GoMag Admin - Mapări SKU</title>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
|
|
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
h1 { color: #333; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
|
|
.status { padding: 10px; border-radius: 4px; margin: 10px 0; }
|
|
.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
|
.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
|
.btn { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; margin: 5px; }
|
|
.btn:hover { background: #0056b3; }
|
|
.table-container { margin-top: 20px; }
|
|
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
|
|
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #ddd; }
|
|
th { background-color: #f8f9fa; font-weight: bold; }
|
|
tr:hover { background-color: #f5f5f5; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🛍️ GoMag Admin - Import Comenzi Web → ROA</h1>
|
|
|
|
<div id="status-area">
|
|
<div class="success">✅ Container Docker activ pe port 5003</div>
|
|
<div id="db-status">🔄 Verificare conexiune Oracle...</div>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<h2>📋 Mapări SKU Active</h2>
|
|
<button class="btn" onclick="loadMappings()">🔄 Reîmprospătează</button>
|
|
<button class="btn" onclick="testConnection()">🔍 Test Conexiune DB</button>
|
|
|
|
<div id="mappings-container">
|
|
<p>Loading...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Test conexiune la load
|
|
window.onload = function() {
|
|
testConnection();
|
|
loadMappings();
|
|
}
|
|
|
|
function testConnection() {
|
|
fetch('/test-db')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const statusDiv = document.getElementById('db-status');
|
|
if (data.success) {
|
|
statusDiv.className = 'status success';
|
|
statusDiv.innerHTML = '✅ Oracle conectat: ' + data.message;
|
|
} else {
|
|
statusDiv.className = 'status error';
|
|
statusDiv.innerHTML = '❌ Eroare Oracle: ' + data.error;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
document.getElementById('db-status').innerHTML = '❌ Eroare fetch: ' + error;
|
|
});
|
|
}
|
|
|
|
function loadMappings() {
|
|
fetch('/api/mappings')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
let html = '<table>';
|
|
html += '<tr><th>SKU</th><th>CODMAT</th><th>Cantitate ROA</th><th>Procent Preț</th><th>Activ</th><th>Data Creare</th></tr>';
|
|
|
|
if (data.mappings && data.mappings.length > 0) {
|
|
data.mappings.forEach(row => {
|
|
const activIcon = row[4] === 1 ? '✅' : '❌';
|
|
html += `<tr>
|
|
<td><strong>${row[0]}</strong></td>
|
|
<td>${row[1]}</td>
|
|
<td>${row[2]}</td>
|
|
<td>${row[3]}%</td>
|
|
<td>${activIcon}</td>
|
|
<td>${new Date(row[5]).toLocaleDateString()}</td>
|
|
</tr>`;
|
|
});
|
|
} else {
|
|
html += '<tr><td colspan="6">Nu există mapări configurate</td></tr>';
|
|
}
|
|
html += '</table>';
|
|
|
|
document.getElementById('mappings-container').innerHTML = html;
|
|
})
|
|
.catch(error => {
|
|
document.getElementById('mappings-container').innerHTML = '❌ Eroare: ' + error;
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
return render_template_string(html_template)
|
|
|
|
@app.route('/test-db')
|
|
def test_db():
|
|
"""Test conexiune Oracle și verificare tabel"""
|
|
try:
|
|
with pool.acquire() as con:
|
|
with con.cursor() as cur:
|
|
# Test conexiune de bază
|
|
cur.execute("SELECT SYSDATE FROM DUAL")
|
|
db_date = cur.fetchone()[0]
|
|
|
|
# Verificare existență tabel ARTICOLE_TERTI
|
|
cur.execute("""
|
|
SELECT COUNT(*) FROM USER_TABLES
|
|
WHERE TABLE_NAME = 'ARTICOLE_TERTI'
|
|
""")
|
|
table_exists = cur.fetchone()[0] > 0
|
|
|
|
if not table_exists:
|
|
return jsonify({
|
|
"success": False,
|
|
"error": "Tabelul ARTICOLE_TERTI nu există. Rulează 01_create_table.sql"
|
|
})
|
|
|
|
# Count records
|
|
cur.execute("SELECT COUNT(*) FROM ARTICOLE_TERTI")
|
|
record_count = cur.fetchone()[0]
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"message": f"DB Time: {db_date}, Records: {record_count}",
|
|
"table_exists": table_exists,
|
|
"record_count": record_count
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Test DB failed: {e}")
|
|
return jsonify({"success": False, "error": str(e)})
|
|
|
|
@app.route('/api/mappings')
|
|
def get_mappings():
|
|
"""Returnează toate mapările SKU active"""
|
|
try:
|
|
with pool.acquire() as con:
|
|
with con.cursor() as cur:
|
|
cur.execute("""
|
|
SELECT sku, codmat, cantitate_roa, procent_pret, activ, data_creare
|
|
FROM ARTICOLE_TERTI
|
|
ORDER BY sku, codmat
|
|
""")
|
|
mappings = cur.fetchall()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"mappings": mappings,
|
|
"count": len(mappings)
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Get mappings failed: {e}")
|
|
return jsonify({"success": False, "error": str(e)})
|
|
|
|
# Inițializare pool la startup
|
|
try:
|
|
pool = start_pool()
|
|
logger.info("Admin interface started successfully")
|
|
except Exception as e:
|
|
logger.error(f"Failed to start admin interface: {e}")
|
|
pool = None
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, debug=True) |