feat: Implement main application shell with navigation and color scheme toggle

- Added Appshell component with responsive navbar and main content area
- Integrated ColorSchemeToggle for light/dark mode switching
- Created Welcome component with styled title and introductory text
- Developed ChatPage for LLM interaction with WebSocket support
- Implemented Biblioteca for managing notes with rich text editor
- Added LoginPage for user authentication with error handling
- Introduced MessageList and MessageBubble components for chat messages
- Styled components with CSS modules for consistent design
This commit is contained in:
2025-06-21 02:01:21 +02:00
parent 3d5deef0fb
commit aef8791151
101 changed files with 169 additions and 166 deletions
+44
View File
@@ -0,0 +1,44 @@
import base64
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
class Encriptar_fernet:
@staticmethod
def _derivar_clave(password: str, salt: bytes) -> bytes:
"""
Deriva una clave segura a partir de la contraseña y el salt usando PBKDF2HMAC.
"""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100_000,
backend=default_backend()
)
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
@classmethod
def encriptar(cls, texto: str, password: str) -> bytes:
"""
Encripta un texto con una clave derivada de la contraseña + salt aleatorio.
El salt es embebido al inicio del token cifrado.
"""
salt = os.urandom(16) # 128 bits de salt aleatorio
key = cls._derivar_clave(password, salt)
fernet = Fernet(key)
token = fernet.encrypt(texto.encode('utf-8'))
return salt + token # Embebemos el salt al principio
@classmethod
def desencriptar(cls, token_con_salt: bytes, password: str) -> str:
"""
Extrae el salt del token, deriva la clave, y desencripta el texto.
"""
salt = token_con_salt[:16]
token = token_con_salt[16:]
key = cls._derivar_clave(password, salt)
fernet = Fernet(key)
return fernet.decrypt(token).decode('utf-8')
+67
View File
@@ -0,0 +1,67 @@
import uuid
import datetime
import re
class GeneradorIDUnico:
def __init__(self, tipo_objeto: str):
if not re.match(r'^[A-Z]{4}$', tipo_objeto):
raise ValueError("El tipo de objeto debe tener 4 letras en mayúscula (ej: ABCD)")
self.tipo_objeto = tipo_objeto
def generar(self):
f = datetime.datetime.now().strftime('%Y%m%d')
u = uuid.uuid4().hex[:9]
n = ''.join(filter(str.isdigit, uuid.uuid4().hex))[:8]
n = n.ljust(8, '0')
t = sum(int(c) for c in f)
t += sum(int(c, 16) for c in u)
t += sum(int(c) for c in n)
c = str(t % 10)
l = t + int(c)
d = hex(l % 16)[-1]
return f"{self.tipo_objeto}{f}-{d}{u}{c}{n}"
@staticmethod
def verificar(id_: str) -> bool:
try:
f = id_[4:12]
cuerpo = id_[13:]
d = cuerpo[0]
u = cuerpo[1:10]
c = cuerpo[10]
n = cuerpo[11:19]
t = sum(int(c) for c in f)
t += sum(int(c, 16) for c in u)
t += sum(int(c) for c in n)
esd = str(t % 10)
l = t + int(esd)
esh = hex(l % 16)[-1]
return (
d.lower() == esh.lower() and
c == esd
)
except Exception:
return False
@staticmethod
def tamaño_bytes_bits(id_: str):
"""Devuelve el tamaño del ID en bytes y bits (UTF-8)"""
bytes_ = len(id_.encode('utf-8'))
return bytes_, bytes_ * 8
if __name__ == "__main__":
# Ejemplo de uso
generador = GeneradorIDUnico("FACT")
nuevo_id = generador.generar()
print(f"Nuevo ID generado: {nuevo_id}")
# Verificación del ID
es_valido = GeneradorIDUnico.verificar(nuevo_id)
print(f"El ID es válido: {es_valido}")
# Tamaño del ID
bytes_, bits_ = GeneradorIDUnico.tamaño_bytes_bits(nuevo_id)
print(f"Tamaño del ID: {bytes_} bytes / {bits_} bits")
View File