Compare commits
3 Commits
53aa755c39
...
7dddbc4764
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dddbc4764 | ||
|
|
f6d8ab13c0 | ||
|
|
1a5778be2e |
@@ -7,7 +7,7 @@
|
|||||||
Desde `c:\word`:
|
Desde `c:\word`:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
python demo/_run_elementor_temp.py
|
python -m demo.app
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verificar que quedo arriba
|
## Verificar que quedo arriba
|
||||||
@@ -27,12 +27,12 @@ Si responde `StatusCode: 200`, esta listo.
|
|||||||
Desde `c:\word`:
|
Desde `c:\word`:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Start-Process -FilePath python -ArgumentList @('demo/_run_elementor_temp.py') -WorkingDirectory 'c:\word' -RedirectStandardOutput 'c:\word\logs_demo_app.txt' -RedirectStandardError 'c:\word\logs_demo_app.err'
|
Start-Process -FilePath python -ArgumentList @('-m','demo.app') -WorkingDirectory 'c:\word'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parar servidor
|
## Parar servidor
|
||||||
```powershell
|
```powershell
|
||||||
Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'python.exe' -and $_.CommandLine -match '_run_elementor_temp.py' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'python.exe' -and $_.CommandLine -match 'demo.app' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Logs
|
## Logs
|
||||||
|
|||||||
@@ -74,6 +74,18 @@
|
|||||||
- Revert:
|
- Revert:
|
||||||
- `git revert dd98e9d`
|
- `git revert dd98e9d`
|
||||||
|
|
||||||
|
### Runtime unificado (app + elementor)
|
||||||
|
- Commit: `1a5778b`
|
||||||
|
- Objetivo: unificar arranque con `python -m demo.app` y registrar blueprint de Elementor en runtime principal.
|
||||||
|
- Revert:
|
||||||
|
- `git revert 1a5778b`
|
||||||
|
|
||||||
|
### Fix SQLite wrapper (arranque sin error SQL)
|
||||||
|
- Commit: `f6d8ab1`
|
||||||
|
- Objetivo: evitar conversiones SQL invalidas en SQLite que rompian inicializacion y generaban reintentos.
|
||||||
|
- Revert:
|
||||||
|
- `git revert f6d8ab1`
|
||||||
|
|
||||||
## URL local canonica (unificada)
|
## URL local canonica (unificada)
|
||||||
- Base local: `http://127.0.0.1:5001`
|
- Base local: `http://127.0.0.1:5001`
|
||||||
- Builder local: `http://127.0.0.1:5001/elementor/1`
|
- Builder local: `http://127.0.0.1:5001/elementor/1`
|
||||||
@@ -81,14 +93,14 @@
|
|||||||
|
|
||||||
## Arranque rapido local (Windows)
|
## Arranque rapido local (Windows)
|
||||||
1. Desde `c:\word`, ejecutar:
|
1. Desde `c:\word`, ejecutar:
|
||||||
- `python demo/_run_elementor_temp.py`
|
- `python -m demo.app`
|
||||||
2. Abrir:
|
2. Abrir:
|
||||||
- `http://127.0.0.1:5001/elementor/1`
|
- `http://127.0.0.1:5001/elementor/1`
|
||||||
3. Verificacion rapida:
|
3. Verificacion rapida:
|
||||||
- `Invoke-WebRequest http://127.0.0.1:5001/elementor/1 -UseBasicParsing`
|
- `Invoke-WebRequest http://127.0.0.1:5001/elementor/1 -UseBasicParsing`
|
||||||
|
|
||||||
Notas:
|
Notas:
|
||||||
- En el primer arranque puede tardar ~40-50 segundos antes de quedar escuchando en `5001`.
|
- En el primer arranque puede tardar unos segundos adicionales por inicializacion de DB.
|
||||||
- Logs:
|
- Logs:
|
||||||
- `c:\word\logs_demo_app.txt`
|
- `c:\word\logs_demo_app.txt`
|
||||||
- `c:\word\logs_demo_app.err`
|
- `c:\word\logs_demo_app.err`
|
||||||
|
|||||||
34
demo/app.py
34
demo/app.py
@@ -1,10 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
GKACHELE™ SaaS PageBuilder - Sistema Modular
|
GKACHELE SaaS PageBuilder - Sistema Modular
|
||||||
© 2025 GKACHELE™. Todos los derechos reservados.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from flask import Flask, jsonify, request
|
import sys
|
||||||
|
from flask import Flask, jsonify
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PROJECT_ROOT = os.path.dirname(BASE_DIR)
|
||||||
|
if PROJECT_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, PROJECT_ROOT)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .config import SECRET_KEY, PORT
|
||||||
|
from .database import init_db
|
||||||
|
from .routes.auth import auth_bp
|
||||||
|
from .routes.dashboard import dashboard_bp
|
||||||
|
from .routes.customizer import customizer_bp
|
||||||
|
from .routes.admin import admin_bp
|
||||||
|
from .routes.public import public_bp
|
||||||
|
except ImportError:
|
||||||
from config import SECRET_KEY, PORT
|
from config import SECRET_KEY, PORT
|
||||||
from database import init_db
|
from database import init_db
|
||||||
from routes.auth import auth_bp
|
from routes.auth import auth_bp
|
||||||
@@ -13,6 +28,11 @@ from routes.customizer import customizer_bp
|
|||||||
from routes.admin import admin_bp
|
from routes.admin import admin_bp
|
||||||
from routes.public import public_bp
|
from routes.public import public_bp
|
||||||
|
|
||||||
|
try:
|
||||||
|
from elementor.routes import elementor_bp
|
||||||
|
except ImportError:
|
||||||
|
elementor_bp = None
|
||||||
|
|
||||||
app = Flask(__name__, template_folder='templates', static_folder='static')
|
app = Flask(__name__, template_folder='templates', static_folder='static')
|
||||||
app.secret_key = SECRET_KEY
|
app.secret_key = SECRET_KEY
|
||||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||||
@@ -26,6 +46,8 @@ app.register_blueprint(dashboard_bp)
|
|||||||
app.register_blueprint(customizer_bp)
|
app.register_blueprint(customizer_bp)
|
||||||
app.register_blueprint(admin_bp)
|
app.register_blueprint(admin_bp)
|
||||||
app.register_blueprint(public_bp)
|
app.register_blueprint(public_bp)
|
||||||
|
if elementor_bp is not None and 'elementor' not in app.blueprints:
|
||||||
|
app.register_blueprint(elementor_bp)
|
||||||
|
|
||||||
# Manejadores de Errores Globales
|
# Manejadores de Errores Globales
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
@@ -34,15 +56,18 @@ def handle_500(e):
|
|||||||
response.status_code = 500
|
response.status_code = 500
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def handle_404(e):
|
def handle_404(e):
|
||||||
return jsonify({'success': False, 'error': 'No encontrado'}), 404
|
return jsonify({'success': False, 'error': 'No encontrado'}), 404
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
@app.errorhandler(Exception)
|
||||||
def handle_exception(e):
|
def handle_exception(e):
|
||||||
print(f"ERROR: EXCEPCION: {e}")
|
print(f"ERROR: EXCEPCION: {e}")
|
||||||
return jsonify({'success': False, 'error': str(e)}), 500
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
# Middleware
|
# Middleware
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def add_header(response):
|
def add_header(response):
|
||||||
@@ -50,6 +75,7 @@ def add_header(response):
|
|||||||
response.headers['Content-Security-Policy'] = "frame-ancestors *;"
|
response.headers['Content-Security-Policy'] = "frame-ancestors *;"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(f"GKACHELE SaaS Modular iniciado en puerto {PORT}")
|
print(f"GKACHELE SaaS Modular iniciado en puerto {PORT}")
|
||||||
app.run(debug=True, host='0.0.0.0', port=PORT)
|
app.run(debug=True, host='0.0.0.0', port=PORT)
|
||||||
|
|||||||
104
demo/db.py
Normal file
104
demo/db.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import json
|
||||||
|
|
||||||
|
# DB Settings (Postgres)
|
||||||
|
DB_HOST = os.environ.get('DB_HOST', None)
|
||||||
|
DB_PORT = int(os.environ.get('DB_PORT', '5432'))
|
||||||
|
DB_NAME = os.environ.get('DB_NAME', 'gkachele')
|
||||||
|
DB_USER = os.environ.get('DB_USER', 'gkachele')
|
||||||
|
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'gkachele_pass')
|
||||||
|
|
||||||
|
# SQLite Settings
|
||||||
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SQLITE_PATH = os.path.join(BASE_DIR, 'database', 'main.db')
|
||||||
|
|
||||||
|
class CursorWrapper:
|
||||||
|
def __init__(self, cursor, is_sqlite=False):
|
||||||
|
self._cursor = cursor
|
||||||
|
self._is_sqlite = is_sqlite
|
||||||
|
|
||||||
|
def execute(self, query, params=None):
|
||||||
|
try:
|
||||||
|
if self._is_sqlite:
|
||||||
|
# Skip postgres-specific maintenance commands
|
||||||
|
if 'setval' in query or 'pg_get_serial_sequence' in query:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Adapt psycopg2-style placeholders (%s) to sqlite (?)
|
||||||
|
if isinstance(query, str) and '%s' in query:
|
||||||
|
query = query.replace('%s', '?')
|
||||||
|
|
||||||
|
# SQLite doesn't support IF NOT EXISTS in ALTER TABLE
|
||||||
|
if 'ALTER TABLE' in query and 'ADD COLUMN' in query and 'IF NOT EXISTS' in query:
|
||||||
|
query = query.replace('IF NOT EXISTS', '')
|
||||||
|
|
||||||
|
# Handle SERIAL PRIMARY KEY and other PG types
|
||||||
|
query = query.replace('SERIAL PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT')
|
||||||
|
if 'IF NOT EXISTS' not in query and 'CREATE TABLE' in query:
|
||||||
|
query = query.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS')
|
||||||
|
else:
|
||||||
|
# Adapt sqlite-style placeholders (?) to psycopg2 (%s)
|
||||||
|
if isinstance(query, str) and '?' in query:
|
||||||
|
query = query.replace('?', '%s')
|
||||||
|
|
||||||
|
if params is None:
|
||||||
|
return self._cursor.execute(query)
|
||||||
|
return self._cursor.execute(query, params)
|
||||||
|
except (sqlite3.OperationalError, sqlite3.IntegrityError) as e:
|
||||||
|
msg = str(e).lower()
|
||||||
|
if "duplicate column name" in msg or "already exists" in msg:
|
||||||
|
return None
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def fetchone(self):
|
||||||
|
return self._cursor.fetchone()
|
||||||
|
|
||||||
|
def fetchall(self):
|
||||||
|
return self._cursor.fetchall()
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self._cursor, name)
|
||||||
|
|
||||||
|
class ConnectionWrapper:
|
||||||
|
def __init__(self, conn, is_sqlite=False):
|
||||||
|
self._conn = conn
|
||||||
|
self._is_sqlite = is_sqlite
|
||||||
|
|
||||||
|
def cursor(self):
|
||||||
|
return CursorWrapper(self._conn.cursor(), is_sqlite=self._is_sqlite)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
return self._conn.commit()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return self._conn.close()
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self._conn, name)
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
if DB_HOST:
|
||||||
|
try:
|
||||||
|
import psycopg2
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
host=DB_HOST,
|
||||||
|
port=DB_PORT,
|
||||||
|
dbname=DB_NAME,
|
||||||
|
user=DB_USER,
|
||||||
|
password=DB_PASSWORD,
|
||||||
|
)
|
||||||
|
return ConnectionWrapper(conn, is_sqlite=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Postgres connection failed: {e}. Falling back to SQLite.")
|
||||||
|
|
||||||
|
# Fallback to SQLite
|
||||||
|
os.makedirs(os.path.dirname(SQLITE_PATH), exist_ok=True)
|
||||||
|
conn = sqlite3.connect(SQLITE_PATH)
|
||||||
|
return ConnectionWrapper(conn, is_sqlite=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import psycopg2
|
||||||
|
IntegrityError = psycopg2.IntegrityError
|
||||||
|
except ImportError:
|
||||||
|
IntegrityError = sqlite3.IntegrityError
|
||||||
Reference in New Issue
Block a user