Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture Tech Stack: - Backend: FastAPI + python-oracledb (Oracle DB integration) - Frontend: Vue.js 3 + PrimeVue + Vite - Telegram Bot: python-telegram-bot + SQLite - Infrastructure: Shared database pool, JWT authentication, SSH tunnel Features: - FastAPI backend with async Oracle connection pool - Vue.js 3 responsive frontend with PrimeVue components - Telegram bot alternative interface - Microservices architecture with shared components - Complete deployment support (Linux Docker + Windows IIS) - Comprehensive testing (Playwright E2E + pytest) Repository Structure: - reports-app/ - Main application (backend, frontend, telegram-bot) - shared/ - Shared components (database pool, auth, utils) - deployment/ - Deployment scripts (Linux & Windows) - docs/ - Project documentation - security/ - Security scanning and git hooks
This commit is contained in:
119
shared/database/oracle_pool.py
Normal file
119
shared/database/oracle_pool.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Oracle Database Connection Pool - Shared între toate aplicațiile ROA2WEB
|
||||
Folosește oracledb cu connection pooling pentru performance optimă
|
||||
"""
|
||||
import oracledb
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OraclePool:
|
||||
"""
|
||||
Singleton class pentru Oracle connection pool
|
||||
Partajat între toate microservicele ROA2WEB
|
||||
"""
|
||||
_instance: Optional['OraclePool'] = None
|
||||
_pool: Optional[oracledb.ConnectionPool] = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(OraclePool, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
async def initialize(self, **config):
|
||||
"""Inițializează pool-ul de conexiuni"""
|
||||
if self._pool is None:
|
||||
# Check if we have DSN or individual parameters
|
||||
dsn = config.get('dsn', os.getenv('ORACLE_DSN'))
|
||||
if dsn:
|
||||
# Use DSN connection
|
||||
self._pool = oracledb.create_pool(
|
||||
user=config.get('user', os.getenv('ORACLE_USER')),
|
||||
password=config.get('password', os.getenv('ORACLE_PASSWORD')),
|
||||
dsn=dsn,
|
||||
min=config.get('min_connections', 2),
|
||||
max=config.get('max_connections', 10),
|
||||
increment=config.get('increment', 1),
|
||||
getmode=oracledb.POOL_GETMODE_WAIT
|
||||
)
|
||||
else:
|
||||
# Use individual parameters (host, port, sid)
|
||||
self._pool = oracledb.create_pool(
|
||||
user=config.get('user', os.getenv('ORACLE_USER')),
|
||||
password=config.get('password', os.getenv('ORACLE_PASSWORD')),
|
||||
host=config.get('host', os.getenv('ORACLE_HOST', 'localhost')),
|
||||
port=config.get('port', int(os.getenv('ORACLE_PORT', '1526'))),
|
||||
sid=config.get('sid', os.getenv('ORACLE_SID', 'ROA')),
|
||||
min=config.get('min_connections', 2),
|
||||
max=config.get('max_connections', 10),
|
||||
increment=config.get('increment', 1),
|
||||
getmode=oracledb.POOL_GETMODE_WAIT
|
||||
)
|
||||
logger.info(f"Oracle pool created with {self._pool.opened} connections")
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_connection(self):
|
||||
"""Context manager pentru obținerea unei conexiuni din pool"""
|
||||
if self._pool is None:
|
||||
raise RuntimeError("Pool not initialized. Call initialize() first.")
|
||||
|
||||
connection = None
|
||||
try:
|
||||
connection = self._pool.acquire()
|
||||
logger.debug("Connection acquired from pool")
|
||||
yield connection
|
||||
finally:
|
||||
if connection is not None:
|
||||
connection.close()
|
||||
logger.debug("Connection returned to pool")
|
||||
|
||||
|
||||
async def execute_query(self, query: str, parameters=None):
|
||||
"""
|
||||
Execute a SQL query and return all results
|
||||
Based on official Oracle python-oracledb patterns
|
||||
"""
|
||||
if self._pool is None:
|
||||
raise RuntimeError("Pool not initialized. Call initialize() first.")
|
||||
|
||||
connection = None
|
||||
try:
|
||||
connection = self._pool.acquire()
|
||||
logger.debug(f"Executing query: {query[:100]}...")
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
if parameters:
|
||||
cursor.execute(query, parameters)
|
||||
else:
|
||||
cursor.execute(query)
|
||||
|
||||
# Check if this is a SELECT statement
|
||||
if query.strip().upper().startswith('SELECT') or query.strip().upper().startswith('WITH'):
|
||||
return cursor.fetchall()
|
||||
else:
|
||||
# For DML statements, return affected row count
|
||||
connection.commit()
|
||||
return cursor.rowcount
|
||||
|
||||
except Exception as e:
|
||||
if connection:
|
||||
connection.rollback()
|
||||
logger.error(f"Query execution failed: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
if connection is not None:
|
||||
connection.close()
|
||||
logger.debug("Connection returned to pool")
|
||||
|
||||
async def close_pool(self):
|
||||
"""Închide pool-ul de conexiuni"""
|
||||
if self._pool is not None:
|
||||
self._pool.close()
|
||||
self._pool = None
|
||||
logger.info("Oracle pool closed")
|
||||
|
||||
# Instance globală pentru folosire în toate aplicațiile
|
||||
oracle_pool = OraclePool()
|
||||
Reference in New Issue
Block a user