Add OpenAI and PostgreSQL credential management
- Implemented OpenAICredencial class for managing OpenAI API keys. - Created OpenAICredencialModel and OpenAICredencialMapper for SQLAlchemy integration. - Developed OpenAICredencialRepo for CRUD operations on OpenAI credentials. - Established OpenAICliente class for interacting with OpenAI API. - Introduced PostgresCredencial class for managing PostgreSQL connection details. - Created PostgresCredencialModel and PostgresCredencialMapper for SQLAlchemy integration. - Developed PostgresCredencialRepo for CRUD operations on PostgreSQL credentials. - Added base connection class and PostgreSQL connection implementation. - Included environment variable loading for sensitive data management.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
class OpenAICredencial:
|
||||
def __init__(self, titulo: str, api_key: str, organizacion: str = None):
|
||||
"""
|
||||
:param titulo: Nombre descriptivo para esta credencial.
|
||||
:param api_key: Clave secreta de la API de OpenAI.
|
||||
:param organizacion: (Opcional) ID de la organización asociada a la cuenta de OpenAI.
|
||||
"""
|
||||
self.titulo = titulo
|
||||
self.api_key = api_key
|
||||
self.organizacion = organizacion
|
||||
|
||||
def get_headers(self) -> dict:
|
||||
"""
|
||||
Retorna los encabezados necesarios para autenticar una petición HTTP a OpenAI.
|
||||
"""
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}"
|
||||
}
|
||||
if self.organizacion:
|
||||
headers["OpenAI-Organization"] = self.organizacion
|
||||
return headers
|
||||
@@ -0,0 +1,92 @@
|
||||
import os
|
||||
import base64
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy import Column, Integer, String
|
||||
|
||||
from src.ConexionSql.Base_conexion import ConexionBase
|
||||
from src.base import Base
|
||||
from src.ApiKeys.openai_apikey import OpenAICredencial
|
||||
from security.Encriptar import Encriptar_fernet
|
||||
from entrypoint import ENV_PATH
|
||||
|
||||
# ----------------------
|
||||
# Cargar clave maestra
|
||||
# ----------------------
|
||||
load_dotenv(ENV_PATH)
|
||||
pssword = os.getenv('MASTER_PASSWORD')
|
||||
if pssword is None:
|
||||
raise ValueError("MASTER_PASSWORD no está definida en el archivo .env")
|
||||
|
||||
# ----------------------
|
||||
# MODELO (SQLAlchemy)
|
||||
# ----------------------
|
||||
|
||||
class OpenAICredencialModel(Base):
|
||||
__tablename__ = 'openai_credenciales'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
titulo = Column(String, nullable=False)
|
||||
api_key = Column(String, nullable=False) # Encriptada como base64 string
|
||||
organizacion = Column(String, nullable=True)
|
||||
|
||||
# ----------------------
|
||||
# MAPPER
|
||||
# ----------------------
|
||||
|
||||
class OpenAICredencialMapper:
|
||||
@staticmethod
|
||||
def to_dict(obj: OpenAICredencial) -> dict:
|
||||
return {
|
||||
"titulo": obj.titulo,
|
||||
"api_key": base64.b64encode(
|
||||
Encriptar_fernet.encriptar(obj.api_key, pssword)
|
||||
).decode('utf-8'),
|
||||
"organizacion": obj.organizacion
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: dict) -> OpenAICredencial:
|
||||
return OpenAICredencial(
|
||||
titulo=data["titulo"],
|
||||
api_key=Encriptar_fernet.desencriptar(
|
||||
base64.b64decode(data["api_key"]), pssword
|
||||
),
|
||||
organizacion=data.get("organizacion")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_model(model: OpenAICredencialModel) -> OpenAICredencial:
|
||||
return OpenAICredencial(
|
||||
titulo=model.titulo,
|
||||
api_key=Encriptar_fernet.desencriptar(
|
||||
base64.b64decode(model.api_key), pssword
|
||||
),
|
||||
organizacion=model.organizacion
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
# REPO
|
||||
# ----------------------
|
||||
|
||||
class OpenAICredencialRepo:
|
||||
def __init__(self, conexion: ConexionBase):
|
||||
self.session = conexion.get_session()
|
||||
|
||||
def add(self, credencial: OpenAICredencial) -> int:
|
||||
data = OpenAICredencialMapper.to_dict(credencial)
|
||||
model = OpenAICredencialModel(**data)
|
||||
self.session.add(model)
|
||||
self.session.commit()
|
||||
return model.id
|
||||
|
||||
def get_all(self) -> list[OpenAICredencial]:
|
||||
models = self.session.query(OpenAICredencialModel).all()
|
||||
return [OpenAICredencialMapper.from_model(m) for m in models]
|
||||
|
||||
def get_by_titulo(self, titulo: str) -> OpenAICredencial | None:
|
||||
model = self.session.query(OpenAICredencialModel).filter_by(titulo=titulo).first()
|
||||
return OpenAICredencialMapper.from_model(model) if model else None
|
||||
|
||||
def get_by_id(self, id_: int) -> OpenAICredencial | None:
|
||||
model = self.session.get(OpenAICredencialModel, id_)
|
||||
return OpenAICredencialMapper.from_model(model) if model else None
|
||||
@@ -0,0 +1,59 @@
|
||||
from openai import OpenAI
|
||||
from src.ApiKeys.openai_apikey import OpenAICredencial
|
||||
|
||||
class OpenAICliente:
|
||||
def __init__(self, credencial: OpenAICredencial):
|
||||
"""
|
||||
Inicializa el cliente de OpenAI con una instancia de OpenAICredencial.
|
||||
"""
|
||||
self.credencial = credencial
|
||||
self.client = OpenAI(api_key=self.credencial.api_key)
|
||||
if self.credencial.organizacion:
|
||||
self.client.organization = self.credencial.organizacion
|
||||
|
||||
# --- Chat Completions ---
|
||||
def chat_completion(self, model: str, messages: list, **kwargs):
|
||||
return self.client.chat.completions.create(model=model, messages=messages, **kwargs)
|
||||
|
||||
# --- Text Completions (legacy) ---
|
||||
def completion(self, model: str, prompt: str, **kwargs):
|
||||
return self.client.completions.create(model=model, prompt=prompt, **kwargs)
|
||||
|
||||
# --- Text Edits ---
|
||||
def edit(self, model: str, input: str, instruction: str, **kwargs):
|
||||
return self.client.edits.create(model=model, input=input, instruction=instruction, **kwargs)
|
||||
|
||||
# --- Embeddings ---
|
||||
def embedding(self, model: str, input: str | list[str], **kwargs):
|
||||
return self.client.embeddings.create(model=model, input=input, **kwargs)
|
||||
|
||||
# --- Moderation ---
|
||||
def moderation(self, input: str | list[str], model="text-moderation-latest", **kwargs):
|
||||
return self.client.moderations.create(input=input, model=model, **kwargs)
|
||||
|
||||
# --- Image Generation ---
|
||||
def image_generate(self, prompt: str, **kwargs):
|
||||
return self.client.images.generate(prompt=prompt, **kwargs)
|
||||
|
||||
# --- Audio Transcription ---
|
||||
def audio_transcribe(self, model: str, file_path: str, **kwargs):
|
||||
with open(file_path, "rb") as f:
|
||||
return self.client.audio.transcriptions.create(model=model, file=f, **kwargs)
|
||||
|
||||
# --- Audio Translation ---
|
||||
def audio_translate(self, model: str, file_path: str, **kwargs):
|
||||
with open(file_path, "rb") as f:
|
||||
return self.client.audio.translations.create(model=model, file=f, **kwargs)
|
||||
|
||||
# --- File Upload ---
|
||||
def file_upload(self, file_path: str, purpose="fine-tune", **kwargs):
|
||||
with open(file_path, "rb") as f:
|
||||
return self.client.files.create(file=f, purpose=purpose, **kwargs)
|
||||
|
||||
# --- File List ---
|
||||
def file_list(self, **kwargs):
|
||||
return self.client.files.list(**kwargs)
|
||||
|
||||
# --- File Delete ---
|
||||
def file_delete(self, file_id: str, **kwargs):
|
||||
return self.client.files.delete(file_id=file_id, **kwargs)
|
||||
@@ -0,0 +1,7 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
class ConexionBase(ABC):
|
||||
@abstractmethod
|
||||
def get_session(self) -> Session:
|
||||
pass
|
||||
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime, timezone
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from src.ConexionSql.Base_conexion import ConexionBase
|
||||
from src.Credenciales.postgres_credencial import PostgresCredencial
|
||||
|
||||
|
||||
class PostgresConexion(ConexionBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.estado = "pendiente"
|
||||
self.timestamp = datetime.now(timezone.utc)
|
||||
|
||||
if args and isinstance(args[0], PostgresCredencial):
|
||||
credencial = args[0]
|
||||
self.host = credencial.host
|
||||
self.port = credencial.port
|
||||
self.dbname = credencial.dbname
|
||||
self.user = credencial.user
|
||||
self.password = credencial.password # se guarda la contraseña
|
||||
uri = credencial.get_uri()
|
||||
else:
|
||||
self.user = kwargs.get("user")
|
||||
self.password = kwargs.get("password") # se guarda la contraseña
|
||||
self.host = kwargs.get("host")
|
||||
self.port = kwargs.get("port", 5432)
|
||||
self.dbname = kwargs.get("db") or kwargs.get("dbname")
|
||||
uri = f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.dbname}"
|
||||
|
||||
self.engine = create_engine(uri)
|
||||
self.SessionLocal = sessionmaker(bind=self.engine)
|
||||
|
||||
def get_session(self):
|
||||
return self.SessionLocal()
|
||||
|
||||
def probar_conexion(self) -> bool:
|
||||
try:
|
||||
with self.engine.connect() as connection:
|
||||
connection.execute(text("SELECT 1"))
|
||||
self.estado = "exito"
|
||||
return True
|
||||
except SQLAlchemyError:
|
||||
self.estado = "fallo"
|
||||
return False
|
||||
@@ -0,0 +1,14 @@
|
||||
class PostgresCredencial:
|
||||
def __init__(self, titulo: str, host: str, port: int, dbname: str, user: str, password: str):
|
||||
self.titulo = titulo
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.dbname = dbname
|
||||
self.user = user
|
||||
self.password = password
|
||||
|
||||
def get_uri(self) -> str:
|
||||
"""
|
||||
Retorna una URI de conexión para PostgreSQL.
|
||||
"""
|
||||
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.dbname}"
|
||||
@@ -0,0 +1,106 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from src.ConexionSql.Base_conexion import ConexionBase
|
||||
from src.base import Base
|
||||
from src.Credenciales.postgres_credencial import PostgresCredencial
|
||||
from security.Encriptar import Encriptar_fernet
|
||||
|
||||
# ----------------------
|
||||
# Cargar clave maestra
|
||||
# ----------------------
|
||||
from entrypoint import ENV_PATH
|
||||
load_dotenv(ENV_PATH)
|
||||
pssword = os.getenv('MASTER_PASSWORD')
|
||||
if pssword is None:
|
||||
raise ValueError("MASTER_PASSWORD no está definida en el archivo .env")
|
||||
|
||||
# ----------------------
|
||||
# MODELO (SQLAlchemy)
|
||||
# ----------------------
|
||||
|
||||
class PostgresCredencialModel(Base):
|
||||
__tablename__ = 'postgres_credenciales'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
titulo = Column(String, nullable=False)
|
||||
host = Column(String, nullable=False)
|
||||
port = Column(Integer, nullable=False)
|
||||
dbname = Column(String, nullable=False)
|
||||
user = Column(String, nullable=False)
|
||||
password = Column(String, nullable=False) # Encriptada como base64 string
|
||||
|
||||
# ----------------------
|
||||
# MAPPER
|
||||
# ----------------------
|
||||
|
||||
import base64
|
||||
|
||||
class PostgresCredencialMapper:
|
||||
@staticmethod
|
||||
def to_dict(obj: PostgresCredencial) -> dict:
|
||||
return {
|
||||
"titulo": obj.titulo,
|
||||
"host": obj.host,
|
||||
"port": obj.port,
|
||||
"dbname": obj.dbname,
|
||||
"user": obj.user,
|
||||
"password": base64.b64encode(
|
||||
Encriptar_fernet.encriptar(obj.password, pssword)
|
||||
).decode('utf-8')
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: dict) -> PostgresCredencial:
|
||||
return PostgresCredencial(
|
||||
titulo=data["titulo"],
|
||||
host=data["host"],
|
||||
port=data["port"],
|
||||
dbname=data["dbname"],
|
||||
user=data["user"],
|
||||
password=Encriptar_fernet.desencriptar(
|
||||
base64.b64decode(data["password"]), pssword
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_model(model: PostgresCredencialModel) -> PostgresCredencial:
|
||||
return PostgresCredencial(
|
||||
titulo=model.titulo,
|
||||
host=model.host,
|
||||
port=model.port,
|
||||
dbname=model.dbname,
|
||||
user=model.user,
|
||||
password=Encriptar_fernet.desencriptar(
|
||||
base64.b64decode(model.password), pssword
|
||||
)
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
# REPO
|
||||
# ----------------------
|
||||
|
||||
class PostgresCredencialRepo:
|
||||
def __init__(self, conexion: ConexionBase):
|
||||
self.session = conexion.get_session()
|
||||
|
||||
def add(self, credencial: PostgresCredencial) -> int:
|
||||
data = PostgresCredencialMapper.to_dict(credencial)
|
||||
model = PostgresCredencialModel(**data)
|
||||
self.session.add(model)
|
||||
self.session.commit()
|
||||
return model.id
|
||||
|
||||
def get_all(self) -> list[PostgresCredencial]:
|
||||
models = self.session.query(PostgresCredencialModel).all()
|
||||
return [PostgresCredencialMapper.from_model(m) for m in models]
|
||||
|
||||
def get_by_titulo(self, titulo: str) -> PostgresCredencial | None:
|
||||
model = self.session.query(PostgresCredencialModel).filter_by(titulo=titulo).first()
|
||||
return PostgresCredencialMapper.from_model(model) if model else None
|
||||
|
||||
def get_by_id(self, id_: int) -> PostgresCredencial | None:
|
||||
model = self.session.get(PostgresCredencialModel, id_)
|
||||
return PostgresCredencialMapper.from_model(model) if model else None
|
||||
@@ -0,0 +1,3 @@
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
Base = declarative_base()
|
||||
Reference in New Issue
Block a user