sqliteplus-enhanced 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqliteplus_enhanced-1.0.0/PKG-INFO +126 -0
- sqliteplus_enhanced-1.0.0/README.md +100 -0
- sqliteplus_enhanced-1.0.0/pyproject.toml +31 -0
- sqliteplus_enhanced-1.0.0/setup.cfg +4 -0
- sqliteplus_enhanced-1.0.0/setup.py +35 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/__init__.py +0 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/cli.py +56 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/core.py +77 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/replication.py +72 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/security.py +104 -0
- sqliteplus_enhanced-1.0.0/sqliteplus/server.py +42 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/PKG-INFO +126 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/SOURCES.txt +18 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/dependency_links.txt +1 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/entry_points.txt +2 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/requires.txt +8 -0
- sqliteplus_enhanced-1.0.0/sqliteplus_enhanced.egg-info/top_level.txt +1 -0
- sqliteplus_enhanced-1.0.0/tests/test2.py +0 -0
- sqliteplus_enhanced-1.0.0/tests/test3.py +79 -0
- sqliteplus_enhanced-1.0.0/tests/test_jwt_decode.py +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: sqliteplus-enhanced
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: SQLite mejorado con cifrado SQLCipher y caché en Redis
|
|
5
|
+
Home-page: https://github.com/Alphonsus411/sqliteplus-enhanced
|
|
6
|
+
Author: Adolfo González Hernández
|
|
7
|
+
Author-email: Adolfo González Hernández <adolfogonzal@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: click
|
|
15
|
+
Requires-Dist: fastapi
|
|
16
|
+
Requires-Dist: uvicorn
|
|
17
|
+
Requires-Dist: redis
|
|
18
|
+
Requires-Dist: bcrypt
|
|
19
|
+
Requires-Dist: PyJWT
|
|
20
|
+
Requires-Dist: pytest
|
|
21
|
+
Requires-Dist: setuptools
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
|
|
27
|
+
# 📌 SQLitePlus - Base de Datos Mejorada con Cifrado y Caché 🚀
|
|
28
|
+
|
|
29
|
+
## 📖 Descripción
|
|
30
|
+
SQLitePlus es una implementación mejorada de SQLite con soporte para:
|
|
31
|
+
- **Cifrado de base de datos con SQLCipher** 🔒
|
|
32
|
+
- **Caché en Redis** para mejorar el rendimiento ⚡
|
|
33
|
+
- **Manejo de concurrencia con threading** 🛠️
|
|
34
|
+
- **Exportación y replicación de datos** 📂
|
|
35
|
+
- **Interfaz CLI para administración desde la terminal** 🖥️
|
|
36
|
+
- **Servidor API con FastAPI** para integración flexible 🌐
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 📦 Instalación
|
|
41
|
+
### **1️⃣ Clonar el Repositorio**
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/tuusuario/sqliteplus.git
|
|
44
|
+
cd sqliteplus
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### **2️⃣ Crear un Entorno Virtual**
|
|
48
|
+
```bash
|
|
49
|
+
python -m venv .venv
|
|
50
|
+
source .venv/bin/activate # En Linux/Mac
|
|
51
|
+
.venv\Scripts\activate # En Windows
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### **3️⃣ Instalar Dependencias**
|
|
55
|
+
```bash
|
|
56
|
+
pip install -r requirements.txt
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🚀 Uso
|
|
62
|
+
|
|
63
|
+
### **🔹 Inicializar la Base de Datos**
|
|
64
|
+
```bash
|
|
65
|
+
python -m sqliteplus.cli init-db
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### **🔹 Ejecutar una Consulta de Escritura**
|
|
69
|
+
```bash
|
|
70
|
+
python -m sqliteplus.cli execute "INSERT INTO logs (action) VALUES ('Test desde CLI')"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### **🔹 Ejecutar una Consulta de Lectura**
|
|
74
|
+
```bash
|
|
75
|
+
python -m sqliteplus.cli fetch "SELECT * FROM logs"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### **🔹 Crear una Copia de Seguridad**
|
|
79
|
+
```bash
|
|
80
|
+
python -m sqliteplus.cli backup
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### **🔹 Exportar una Tabla a CSV**
|
|
84
|
+
```bash
|
|
85
|
+
python -m sqliteplus.cli export-csv logs logs_export.csv
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## ⚙️ Configuración de Redis (Opcional pero Recomendado)
|
|
91
|
+
Si deseas habilitar la caché en Redis:
|
|
92
|
+
1. **Iniciar Redis en Local**
|
|
93
|
+
```bash
|
|
94
|
+
redis-server
|
|
95
|
+
```
|
|
96
|
+
2. **Verificar que Redis está activo**
|
|
97
|
+
```bash
|
|
98
|
+
redis-cli ping # Debería responder con 'PONG'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 📡 Uso del Servidor API
|
|
104
|
+
### **🔹 Iniciar el Servidor FastAPI**
|
|
105
|
+
```bash
|
|
106
|
+
uvicorn sqliteplus.server:app --reload --host 0.0.0.0 --port 8000
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### **🔹 Acceder a la Documentación Interactiva**
|
|
110
|
+
Abre en tu navegador: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 📜 Licencia
|
|
115
|
+
Este proyecto está bajo la licencia **MIT**.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🤝 Contribuciones
|
|
120
|
+
¡Las contribuciones son bienvenidas! Si deseas mejorar SQLitePlus, haz un fork del repositorio y envía un pull request. 🚀
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 📧 Contacto
|
|
125
|
+
Si tienes dudas o sugerencias, puedes contactarme en **[tuemail@example.com](mailto:tuemail@example.com)** o abrir un issue en GitHub.
|
|
126
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 📌 SQLitePlus - Base de Datos Mejorada con Cifrado y Caché 🚀
|
|
2
|
+
|
|
3
|
+
## 📖 Descripción
|
|
4
|
+
SQLitePlus es una implementación mejorada de SQLite con soporte para:
|
|
5
|
+
- **Cifrado de base de datos con SQLCipher** 🔒
|
|
6
|
+
- **Caché en Redis** para mejorar el rendimiento ⚡
|
|
7
|
+
- **Manejo de concurrencia con threading** 🛠️
|
|
8
|
+
- **Exportación y replicación de datos** 📂
|
|
9
|
+
- **Interfaz CLI para administración desde la terminal** 🖥️
|
|
10
|
+
- **Servidor API con FastAPI** para integración flexible 🌐
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 📦 Instalación
|
|
15
|
+
### **1️⃣ Clonar el Repositorio**
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/tuusuario/sqliteplus.git
|
|
18
|
+
cd sqliteplus
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### **2️⃣ Crear un Entorno Virtual**
|
|
22
|
+
```bash
|
|
23
|
+
python -m venv .venv
|
|
24
|
+
source .venv/bin/activate # En Linux/Mac
|
|
25
|
+
.venv\Scripts\activate # En Windows
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### **3️⃣ Instalar Dependencias**
|
|
29
|
+
```bash
|
|
30
|
+
pip install -r requirements.txt
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🚀 Uso
|
|
36
|
+
|
|
37
|
+
### **🔹 Inicializar la Base de Datos**
|
|
38
|
+
```bash
|
|
39
|
+
python -m sqliteplus.cli init-db
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### **🔹 Ejecutar una Consulta de Escritura**
|
|
43
|
+
```bash
|
|
44
|
+
python -m sqliteplus.cli execute "INSERT INTO logs (action) VALUES ('Test desde CLI')"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### **🔹 Ejecutar una Consulta de Lectura**
|
|
48
|
+
```bash
|
|
49
|
+
python -m sqliteplus.cli fetch "SELECT * FROM logs"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### **🔹 Crear una Copia de Seguridad**
|
|
53
|
+
```bash
|
|
54
|
+
python -m sqliteplus.cli backup
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### **🔹 Exportar una Tabla a CSV**
|
|
58
|
+
```bash
|
|
59
|
+
python -m sqliteplus.cli export-csv logs logs_export.csv
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## ⚙️ Configuración de Redis (Opcional pero Recomendado)
|
|
65
|
+
Si deseas habilitar la caché en Redis:
|
|
66
|
+
1. **Iniciar Redis en Local**
|
|
67
|
+
```bash
|
|
68
|
+
redis-server
|
|
69
|
+
```
|
|
70
|
+
2. **Verificar que Redis está activo**
|
|
71
|
+
```bash
|
|
72
|
+
redis-cli ping # Debería responder con 'PONG'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 📡 Uso del Servidor API
|
|
78
|
+
### **🔹 Iniciar el Servidor FastAPI**
|
|
79
|
+
```bash
|
|
80
|
+
uvicorn sqliteplus.server:app --reload --host 0.0.0.0 --port 8000
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### **🔹 Acceder a la Documentación Interactiva**
|
|
84
|
+
Abre en tu navegador: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 📜 Licencia
|
|
89
|
+
Este proyecto está bajo la licencia **MIT**.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🤝 Contribuciones
|
|
94
|
+
¡Las contribuciones son bienvenidas! Si deseas mejorar SQLitePlus, haz un fork del repositorio y envía un pull request. 🚀
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 📧 Contacto
|
|
99
|
+
Si tienes dudas o sugerencias, puedes contactarme en **[tuemail@example.com](mailto:tuemail@example.com)** o abrir un issue en GitHub.
|
|
100
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sqliteplus-enhanced"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "SQLite mejorado con cifrado SQLCipher y caché en Redis"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Adolfo González Hernández", email = "adolfogonzal@gmail.com" }
|
|
11
|
+
]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
dependencies = [
|
|
14
|
+
"click",
|
|
15
|
+
"fastapi",
|
|
16
|
+
"uvicorn",
|
|
17
|
+
"redis",
|
|
18
|
+
"bcrypt",
|
|
19
|
+
"PyJWT",
|
|
20
|
+
"pytest",
|
|
21
|
+
"setuptools"
|
|
22
|
+
]
|
|
23
|
+
readme = "README.md"
|
|
24
|
+
requires-python = ">=3.7"
|
|
25
|
+
dynamic = ["classifiers"]
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
sqliteplus = "sqliteplus.cli:cli"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
packages = ["sqliteplus"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="sqliteplus-enhanced",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
author="Adolfo González Hernández",
|
|
7
|
+
author_email="adolfogonzal@gmail.com",
|
|
8
|
+
description="SQLite mejorado con cifrado SQLCipher y caché en Redis",
|
|
9
|
+
long_description=open("README.md", encoding="utf-8").read(),
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
url="https://github.com/Alphonsus411/sqliteplus-enhanced",
|
|
12
|
+
packages=find_packages(),
|
|
13
|
+
include_package_data=True,
|
|
14
|
+
install_requires=[
|
|
15
|
+
"click",
|
|
16
|
+
"fastapi",
|
|
17
|
+
"uvicorn",
|
|
18
|
+
"redis",
|
|
19
|
+
"bcrypt",
|
|
20
|
+
"PyJWT",
|
|
21
|
+
"pytest",
|
|
22
|
+
"setuptools"
|
|
23
|
+
],
|
|
24
|
+
entry_points={
|
|
25
|
+
"console_scripts": [
|
|
26
|
+
"sqliteplus-enhanced=sqliteplus.cli:cli"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
classifiers=[
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"License :: OSI Approved :: MIT License",
|
|
32
|
+
"Operating System :: OS Independent",
|
|
33
|
+
],
|
|
34
|
+
python_requires=">=3.7",
|
|
35
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from sqliteplus.core import SQLitePlus
|
|
3
|
+
from sqliteplus.replication import SQLiteReplication
|
|
4
|
+
|
|
5
|
+
@click.group()
|
|
6
|
+
def cli():
|
|
7
|
+
"""Interfaz de Línea de Comandos para SQLitePlus."""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
@click.command()
|
|
11
|
+
def init_db():
|
|
12
|
+
"""Inicializa la base de datos SQLitePlus."""
|
|
13
|
+
db = SQLitePlus()
|
|
14
|
+
db.log_action("Inicialización de la base de datos desde CLI")
|
|
15
|
+
click.echo("Base de datos inicializada correctamente.")
|
|
16
|
+
|
|
17
|
+
@click.command()
|
|
18
|
+
@click.argument("query")
|
|
19
|
+
def execute(query):
|
|
20
|
+
"""Ejecuta una consulta SQL de escritura."""
|
|
21
|
+
db = SQLitePlus()
|
|
22
|
+
result = db.execute_query(query)
|
|
23
|
+
click.echo(f"Consulta ejecutada. ID insertado: {result}")
|
|
24
|
+
|
|
25
|
+
@click.command()
|
|
26
|
+
@click.argument("query")
|
|
27
|
+
def fetch(query):
|
|
28
|
+
"""Ejecuta una consulta SQL de lectura."""
|
|
29
|
+
db = SQLitePlus()
|
|
30
|
+
result = db.fetch_query(query)
|
|
31
|
+
click.echo(result)
|
|
32
|
+
|
|
33
|
+
@click.command()
|
|
34
|
+
@click.argument("table_name")
|
|
35
|
+
@click.argument("output_file")
|
|
36
|
+
def export_csv(table_name, output_file):
|
|
37
|
+
"""Exporta una tabla a CSV."""
|
|
38
|
+
replicator = SQLiteReplication()
|
|
39
|
+
replicator.export_to_csv(table_name, output_file)
|
|
40
|
+
click.echo(f"Tabla {table_name} exportada a {output_file}")
|
|
41
|
+
|
|
42
|
+
@click.command()
|
|
43
|
+
def backup():
|
|
44
|
+
"""Crea un respaldo de la base de datos."""
|
|
45
|
+
replicator = SQLiteReplication()
|
|
46
|
+
replicator.backup_database()
|
|
47
|
+
click.echo("Copia de seguridad creada correctamente.")
|
|
48
|
+
|
|
49
|
+
cli.add_command(init_db)
|
|
50
|
+
cli.add_command(execute)
|
|
51
|
+
cli.add_command(fetch)
|
|
52
|
+
cli.add_command(export_csv)
|
|
53
|
+
cli.add_command(backup)
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
cli()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SQLitePlus:
|
|
6
|
+
"""
|
|
7
|
+
Manejador de SQLite mejorado con soporte para concurrencia y manejo seguro de consultas.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, db_path="database.db"):
|
|
11
|
+
self.db_path = db_path
|
|
12
|
+
self.lock = threading.Lock() # Para manejar concurrencia
|
|
13
|
+
self._initialize_db()
|
|
14
|
+
|
|
15
|
+
def _initialize_db(self):
|
|
16
|
+
"""
|
|
17
|
+
Inicializa la base de datos creando las tablas necesarias si no existen.
|
|
18
|
+
"""
|
|
19
|
+
with self.get_connection() as conn:
|
|
20
|
+
cursor = conn.cursor()
|
|
21
|
+
cursor.execute(
|
|
22
|
+
"""
|
|
23
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
24
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
|
+
action TEXT,
|
|
26
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
27
|
+
)
|
|
28
|
+
"""
|
|
29
|
+
)
|
|
30
|
+
conn.commit()
|
|
31
|
+
|
|
32
|
+
def get_connection(self):
|
|
33
|
+
"""
|
|
34
|
+
Obtiene una conexión a la base de datos.
|
|
35
|
+
"""
|
|
36
|
+
return sqlite3.connect(self.db_path, check_same_thread=False)
|
|
37
|
+
|
|
38
|
+
def execute_query(self, query, params=()):
|
|
39
|
+
"""
|
|
40
|
+
Ejecuta una consulta de escritura en la base de datos.
|
|
41
|
+
"""
|
|
42
|
+
with self.lock: # Bloquea la ejecución para evitar conflictos
|
|
43
|
+
with self.get_connection() as conn:
|
|
44
|
+
cursor = conn.cursor()
|
|
45
|
+
try:
|
|
46
|
+
cursor.execute(query, params)
|
|
47
|
+
conn.commit()
|
|
48
|
+
return cursor.lastrowid
|
|
49
|
+
except sqlite3.Error as e:
|
|
50
|
+
print(f"Error en la consulta: {e}")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
def fetch_query(self, query, params=()):
|
|
54
|
+
"""
|
|
55
|
+
Ejecuta una consulta de lectura en la base de datos y devuelve los resultados.
|
|
56
|
+
"""
|
|
57
|
+
with self.lock:
|
|
58
|
+
with self.get_connection() as conn:
|
|
59
|
+
cursor = conn.cursor()
|
|
60
|
+
try:
|
|
61
|
+
cursor.execute(query, params)
|
|
62
|
+
return cursor.fetchall()
|
|
63
|
+
except sqlite3.Error as e:
|
|
64
|
+
print(f"Error en la consulta: {e}")
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def log_action(self, action):
|
|
68
|
+
"""
|
|
69
|
+
Registra una acción en la tabla de logs.
|
|
70
|
+
"""
|
|
71
|
+
self.execute_query("INSERT INTO logs (action) VALUES (?)", (action,))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
db = SQLitePlus()
|
|
76
|
+
db.log_action("Inicialización del sistema")
|
|
77
|
+
print("SQLitePlus está listo para usar.")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from sqliteplus.core import SQLitePlus
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SQLiteReplication:
|
|
8
|
+
"""
|
|
9
|
+
Módulo para exportación y replicación de bases de datos SQLitePlus.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, db_path="database.db", backup_dir="backups"):
|
|
13
|
+
self.db_path = db_path
|
|
14
|
+
self.backup_dir = backup_dir
|
|
15
|
+
os.makedirs(self.backup_dir, exist_ok=True)
|
|
16
|
+
|
|
17
|
+
def export_to_csv(self, table_name: str, output_file: str):
|
|
18
|
+
"""
|
|
19
|
+
Exporta los datos de una tabla a un archivo CSV.
|
|
20
|
+
"""
|
|
21
|
+
conn = SQLitePlus().get_connection()
|
|
22
|
+
cursor = conn.cursor()
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
cursor.execute(f"SELECT * FROM {table_name}")
|
|
26
|
+
rows = cursor.fetchall()
|
|
27
|
+
column_names = [desc[0] for desc in cursor.description]
|
|
28
|
+
|
|
29
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
30
|
+
f.write(",".join(column_names) + "\n")
|
|
31
|
+
for row in rows:
|
|
32
|
+
f.write(",".join(map(str, row)) + "\n")
|
|
33
|
+
|
|
34
|
+
print(f"Datos exportados correctamente a {output_file}")
|
|
35
|
+
except sqlite3.Error as e:
|
|
36
|
+
print(f"Error al exportar datos: {e}")
|
|
37
|
+
finally:
|
|
38
|
+
conn.close()
|
|
39
|
+
|
|
40
|
+
def backup_database(self):
|
|
41
|
+
"""
|
|
42
|
+
Crea una copia de seguridad de la base de datos.
|
|
43
|
+
"""
|
|
44
|
+
backup_file = os.path.join(self.backup_dir, f"backup_{self._get_timestamp()}.db")
|
|
45
|
+
try:
|
|
46
|
+
shutil.copy2(self.db_path, backup_file)
|
|
47
|
+
print(f"Copia de seguridad creada en {backup_file}")
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"Error al realizar la copia de seguridad: {e}")
|
|
50
|
+
|
|
51
|
+
def replicate_database(self, target_db_path: str):
|
|
52
|
+
"""
|
|
53
|
+
Replica la base de datos en otra ubicación.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
shutil.copy2(self.db_path, target_db_path)
|
|
57
|
+
print(f"Base de datos replicada en {target_db_path}")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"Error en la replicación: {e}")
|
|
60
|
+
|
|
61
|
+
def _get_timestamp(self):
|
|
62
|
+
"""
|
|
63
|
+
Genera un timestamp para los nombres de archivo.
|
|
64
|
+
"""
|
|
65
|
+
import datetime
|
|
66
|
+
return datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
replicator = SQLiteReplication()
|
|
71
|
+
replicator.backup_database()
|
|
72
|
+
replicator.export_to_csv("logs", "logs_export.csv")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import threading
|
|
3
|
+
import os
|
|
4
|
+
import functools
|
|
5
|
+
import redis
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SQLitePlus:
|
|
9
|
+
"""
|
|
10
|
+
Manejador de SQLite mejorado con soporte para concurrencia, caché en Redis y cifrado con SQLCipher.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, db_path="database.db", redis_host="localhost", redis_port=6379):
|
|
14
|
+
self.db_path = db_path
|
|
15
|
+
self.lock = threading.Lock() # Para manejar concurrencia
|
|
16
|
+
self.db_key = os.getenv("SQLITE_DB_KEY", "clave_super_segura") # Clave de cifrado
|
|
17
|
+
self.redis_client = redis.Redis(host=redis_host, port=redis_port, db=0) # Configuración de caché
|
|
18
|
+
self._initialize_db()
|
|
19
|
+
|
|
20
|
+
def _initialize_db(self):
|
|
21
|
+
"""
|
|
22
|
+
Inicializa la base de datos creando las tablas necesarias si no existen.
|
|
23
|
+
"""
|
|
24
|
+
with self.get_connection() as conn:
|
|
25
|
+
cursor = conn.cursor()
|
|
26
|
+
cursor.execute(
|
|
27
|
+
"""
|
|
28
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
action TEXT,
|
|
31
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
)
|
|
33
|
+
"""
|
|
34
|
+
)
|
|
35
|
+
conn.commit()
|
|
36
|
+
|
|
37
|
+
def get_connection(self):
|
|
38
|
+
"""
|
|
39
|
+
Obtiene una conexión cifrada a la base de datos usando SQLCipher.
|
|
40
|
+
"""
|
|
41
|
+
conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
|
42
|
+
cursor = conn.cursor()
|
|
43
|
+
cursor.execute(f"PRAGMA key='{self.db_key}';") # Aplica la clave de cifrado
|
|
44
|
+
return conn
|
|
45
|
+
|
|
46
|
+
def cache_result(func):
|
|
47
|
+
"""
|
|
48
|
+
Decorador para almacenar en caché los resultados de las consultas SELECT.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
@functools.wraps(func)
|
|
52
|
+
def wrapper(self, query, params=()):
|
|
53
|
+
cache_key = f"query:{query}:{params}"
|
|
54
|
+
cached_result = self.redis_client.get(cache_key)
|
|
55
|
+
if cached_result:
|
|
56
|
+
return eval(cached_result.decode()) # Convertir de string a lista
|
|
57
|
+
result = func(self, query, params)
|
|
58
|
+
if result:
|
|
59
|
+
self.redis_client.setex(cache_key, 300, str(result)) # Caché por 5 minutos
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
return wrapper
|
|
63
|
+
|
|
64
|
+
def execute_query(self, query, params=()):
|
|
65
|
+
"""
|
|
66
|
+
Ejecuta una consulta de escritura en la base de datos.
|
|
67
|
+
"""
|
|
68
|
+
with self.lock: # Bloquea la ejecución para evitar conflictos
|
|
69
|
+
with self.get_connection() as conn:
|
|
70
|
+
cursor = conn.cursor()
|
|
71
|
+
try:
|
|
72
|
+
cursor.execute(query, params)
|
|
73
|
+
conn.commit()
|
|
74
|
+
return cursor.lastrowid
|
|
75
|
+
except sqlite3.Error as e:
|
|
76
|
+
print(f"Error en la consulta: {e}")
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
@cache_result
|
|
80
|
+
def fetch_query(self, query, params=()):
|
|
81
|
+
"""
|
|
82
|
+
Ejecuta una consulta de lectura en la base de datos y devuelve los resultados, con caché en Redis.
|
|
83
|
+
"""
|
|
84
|
+
with self.lock:
|
|
85
|
+
with self.get_connection() as conn:
|
|
86
|
+
cursor = conn.cursor()
|
|
87
|
+
try:
|
|
88
|
+
cursor.execute(query, params)
|
|
89
|
+
return cursor.fetchall()
|
|
90
|
+
except sqlite3.Error as e:
|
|
91
|
+
print(f"Error en la consulta: {e}")
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
def log_action(self, action):
|
|
95
|
+
"""
|
|
96
|
+
Registra una acción en la tabla de logs.
|
|
97
|
+
"""
|
|
98
|
+
self.execute_query("INSERT INTO logs (action) VALUES (?)", (action,))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
db = SQLitePlus()
|
|
103
|
+
db.log_action("Inicialización del sistema con SQLCipher y caché en Redis")
|
|
104
|
+
print("SQLitePlus está listo para usar con cifrado SQLCipher y caché optimizada en Redis.")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from fastapi import FastAPI, HTTPException
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from sqliteplus.core import SQLitePlus
|
|
4
|
+
|
|
5
|
+
# Inicializar FastAPI y la base de datos
|
|
6
|
+
app = FastAPI()
|
|
7
|
+
db = SQLitePlus()
|
|
8
|
+
|
|
9
|
+
# Modelo de datos para recibir consultas SQL
|
|
10
|
+
class QueryModel(BaseModel):
|
|
11
|
+
query: str
|
|
12
|
+
params: list = []
|
|
13
|
+
|
|
14
|
+
@app.get("/")
|
|
15
|
+
def home():
|
|
16
|
+
return {"message": "API SQLitePlus en funcionamiento"}
|
|
17
|
+
|
|
18
|
+
@app.post("/execute/")
|
|
19
|
+
def execute_query(query_data: QueryModel):
|
|
20
|
+
"""
|
|
21
|
+
Ejecuta una consulta SQL de escritura (INSERT, UPDATE, DELETE).
|
|
22
|
+
"""
|
|
23
|
+
result = db.execute_query(query_data.query, tuple(query_data.params))
|
|
24
|
+
if result is None:
|
|
25
|
+
raise HTTPException(status_code=400, detail="Error al ejecutar la consulta")
|
|
26
|
+
return {"message": "Consulta ejecutada con éxito", "last_inserted_id": result}
|
|
27
|
+
|
|
28
|
+
@app.post("/fetch/")
|
|
29
|
+
def fetch_query(query_data: QueryModel):
|
|
30
|
+
"""
|
|
31
|
+
Ejecuta una consulta SQL de lectura (SELECT) y devuelve los resultados.
|
|
32
|
+
"""
|
|
33
|
+
result = db.fetch_query(query_data.query, tuple(query_data.params))
|
|
34
|
+
if result is None:
|
|
35
|
+
raise HTTPException(status_code=400, detail="Error al ejecutar la consulta")
|
|
36
|
+
return {"data": result}
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
import uvicorn
|
|
40
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
41
|
+
|
|
42
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: sqliteplus-enhanced
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: SQLite mejorado con cifrado SQLCipher y caché en Redis
|
|
5
|
+
Home-page: https://github.com/Alphonsus411/sqliteplus-enhanced
|
|
6
|
+
Author: Adolfo González Hernández
|
|
7
|
+
Author-email: Adolfo González Hernández <adolfogonzal@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: click
|
|
15
|
+
Requires-Dist: fastapi
|
|
16
|
+
Requires-Dist: uvicorn
|
|
17
|
+
Requires-Dist: redis
|
|
18
|
+
Requires-Dist: bcrypt
|
|
19
|
+
Requires-Dist: PyJWT
|
|
20
|
+
Requires-Dist: pytest
|
|
21
|
+
Requires-Dist: setuptools
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
|
|
27
|
+
# 📌 SQLitePlus - Base de Datos Mejorada con Cifrado y Caché 🚀
|
|
28
|
+
|
|
29
|
+
## 📖 Descripción
|
|
30
|
+
SQLitePlus es una implementación mejorada de SQLite con soporte para:
|
|
31
|
+
- **Cifrado de base de datos con SQLCipher** 🔒
|
|
32
|
+
- **Caché en Redis** para mejorar el rendimiento ⚡
|
|
33
|
+
- **Manejo de concurrencia con threading** 🛠️
|
|
34
|
+
- **Exportación y replicación de datos** 📂
|
|
35
|
+
- **Interfaz CLI para administración desde la terminal** 🖥️
|
|
36
|
+
- **Servidor API con FastAPI** para integración flexible 🌐
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 📦 Instalación
|
|
41
|
+
### **1️⃣ Clonar el Repositorio**
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/tuusuario/sqliteplus.git
|
|
44
|
+
cd sqliteplus
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### **2️⃣ Crear un Entorno Virtual**
|
|
48
|
+
```bash
|
|
49
|
+
python -m venv .venv
|
|
50
|
+
source .venv/bin/activate # En Linux/Mac
|
|
51
|
+
.venv\Scripts\activate # En Windows
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### **3️⃣ Instalar Dependencias**
|
|
55
|
+
```bash
|
|
56
|
+
pip install -r requirements.txt
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🚀 Uso
|
|
62
|
+
|
|
63
|
+
### **🔹 Inicializar la Base de Datos**
|
|
64
|
+
```bash
|
|
65
|
+
python -m sqliteplus.cli init-db
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### **🔹 Ejecutar una Consulta de Escritura**
|
|
69
|
+
```bash
|
|
70
|
+
python -m sqliteplus.cli execute "INSERT INTO logs (action) VALUES ('Test desde CLI')"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### **🔹 Ejecutar una Consulta de Lectura**
|
|
74
|
+
```bash
|
|
75
|
+
python -m sqliteplus.cli fetch "SELECT * FROM logs"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### **🔹 Crear una Copia de Seguridad**
|
|
79
|
+
```bash
|
|
80
|
+
python -m sqliteplus.cli backup
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### **🔹 Exportar una Tabla a CSV**
|
|
84
|
+
```bash
|
|
85
|
+
python -m sqliteplus.cli export-csv logs logs_export.csv
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## ⚙️ Configuración de Redis (Opcional pero Recomendado)
|
|
91
|
+
Si deseas habilitar la caché en Redis:
|
|
92
|
+
1. **Iniciar Redis en Local**
|
|
93
|
+
```bash
|
|
94
|
+
redis-server
|
|
95
|
+
```
|
|
96
|
+
2. **Verificar que Redis está activo**
|
|
97
|
+
```bash
|
|
98
|
+
redis-cli ping # Debería responder con 'PONG'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 📡 Uso del Servidor API
|
|
104
|
+
### **🔹 Iniciar el Servidor FastAPI**
|
|
105
|
+
```bash
|
|
106
|
+
uvicorn sqliteplus.server:app --reload --host 0.0.0.0 --port 8000
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### **🔹 Acceder a la Documentación Interactiva**
|
|
110
|
+
Abre en tu navegador: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 📜 Licencia
|
|
115
|
+
Este proyecto está bajo la licencia **MIT**.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🤝 Contribuciones
|
|
120
|
+
¡Las contribuciones son bienvenidas! Si deseas mejorar SQLitePlus, haz un fork del repositorio y envía un pull request. 🚀
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 📧 Contacto
|
|
125
|
+
Si tienes dudas o sugerencias, puedes contactarme en **[tuemail@example.com](mailto:tuemail@example.com)** o abrir un issue en GitHub.
|
|
126
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
sqliteplus/__init__.py
|
|
5
|
+
sqliteplus/cli.py
|
|
6
|
+
sqliteplus/core.py
|
|
7
|
+
sqliteplus/replication.py
|
|
8
|
+
sqliteplus/security.py
|
|
9
|
+
sqliteplus/server.py
|
|
10
|
+
sqliteplus_enhanced.egg-info/PKG-INFO
|
|
11
|
+
sqliteplus_enhanced.egg-info/SOURCES.txt
|
|
12
|
+
sqliteplus_enhanced.egg-info/dependency_links.txt
|
|
13
|
+
sqliteplus_enhanced.egg-info/entry_points.txt
|
|
14
|
+
sqliteplus_enhanced.egg-info/requires.txt
|
|
15
|
+
sqliteplus_enhanced.egg-info/top_level.txt
|
|
16
|
+
tests/test2.py
|
|
17
|
+
tests/test3.py
|
|
18
|
+
tests/test_jwt_decode.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sqliteplus
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import bcrypt
|
|
3
|
+
import jwt
|
|
4
|
+
import datetime
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
SECRET_KEY = "supersecreto" # Clave secreta para firmar los JWT
|
|
8
|
+
DB_KEY = os.getenv("SQLITE_DB_KEY", "clave_super_segura") # Clave de cifrado para SQLCipher
|
|
9
|
+
|
|
10
|
+
def hash_password(password: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Genera un hash seguro de la contraseña.
|
|
13
|
+
"""
|
|
14
|
+
salt = bcrypt.gensalt()
|
|
15
|
+
return bcrypt.hashpw(password.encode(), salt).decode()
|
|
16
|
+
|
|
17
|
+
def verify_password(password: str, hashed_password: str) -> bool:
|
|
18
|
+
"""
|
|
19
|
+
Verifica si la contraseña coincide con el hash almacenado.
|
|
20
|
+
"""
|
|
21
|
+
return bcrypt.checkpw(password.encode(), hashed_password.encode())
|
|
22
|
+
|
|
23
|
+
def generate_jwt(user_id: int, role: str) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Genera un token JWT válido.
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
payload = {
|
|
29
|
+
"sub": user_id,
|
|
30
|
+
"role": role,
|
|
31
|
+
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=2)
|
|
32
|
+
}
|
|
33
|
+
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(f"Error generando JWT: {e}")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
def decode_jwt(token: str):
|
|
39
|
+
"""
|
|
40
|
+
Decodifica y verifica un JWT.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
return jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
|
|
44
|
+
except jwt.ExpiredSignatureError:
|
|
45
|
+
return None
|
|
46
|
+
except jwt.InvalidTokenError:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def get_encrypted_connection(db_path="database.db"):
|
|
50
|
+
"""
|
|
51
|
+
Obtiene una conexión cifrada con SQLCipher.
|
|
52
|
+
"""
|
|
53
|
+
conn = sqlite3.connect(db_path)
|
|
54
|
+
cursor = conn.cursor()
|
|
55
|
+
cursor.execute(f"PRAGMA key='{DB_KEY}';") # Establece la clave de cifrado
|
|
56
|
+
return conn
|
|
57
|
+
|
|
58
|
+
def create_users_table(db_path="database.db"):
|
|
59
|
+
"""
|
|
60
|
+
Crea la tabla de usuarios si no existe, usando una base de datos cifrada.
|
|
61
|
+
"""
|
|
62
|
+
conn = get_encrypted_connection(db_path)
|
|
63
|
+
cursor = conn.cursor()
|
|
64
|
+
cursor.execute(
|
|
65
|
+
"""
|
|
66
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
67
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
68
|
+
username TEXT UNIQUE,
|
|
69
|
+
password TEXT,
|
|
70
|
+
role TEXT
|
|
71
|
+
)
|
|
72
|
+
"""
|
|
73
|
+
)
|
|
74
|
+
conn.commit()
|
|
75
|
+
conn.close()
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
create_users_table()
|
|
79
|
+
print("Módulo de seguridad inicializado con cifrado SQLCipher y corrección en JWT.")
|
|
File without changes
|