Compare commits
41 Commits
53aa755c39
...
builder-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdebdf9ddf | ||
|
|
9cccbab1c8 | ||
|
|
864846da0f | ||
|
|
02e32c9673 | ||
|
|
40738cc65b | ||
|
|
d18a92d017 | ||
|
|
10d8fb8cae | ||
|
|
14eca53c91 | ||
|
|
0787c7bc46 | ||
|
|
f7a1c2dffc | ||
|
|
2cc845eb4d | ||
|
|
6c1ccc0a48 | ||
|
|
846ad5c7c6 | ||
|
|
48742a2736 | ||
|
|
208dca9f05 | ||
|
|
93d046a24c | ||
|
|
b37d2d4bec | ||
|
|
a15e3e17af | ||
|
|
d5f2b819bf | ||
|
|
b3bb7d57aa | ||
|
|
e83e915584 | ||
|
|
e3a1c9d17f | ||
|
|
8ae0017533 | ||
|
|
8ac360b69d | ||
|
|
6f143089b4 | ||
|
|
685659c0f1 | ||
|
|
f363eefdca | ||
|
|
400a8230b5 | ||
|
|
f9f7d23b8d | ||
|
|
f2fbc6eedd | ||
|
|
a6089ee341 | ||
|
|
f8935e7c00 | ||
|
|
e5df6de8fc | ||
|
|
df641372fa | ||
|
|
e20f0867fe | ||
|
|
22fcd505f4 | ||
|
|
c2ee81d202 | ||
|
|
b6fb4dadff | ||
|
|
7dddbc4764 | ||
|
|
f6d8ab13c0 | ||
|
|
1a5778be2e |
@@ -7,7 +7,7 @@
|
||||
Desde `c:\word`:
|
||||
|
||||
```powershell
|
||||
python demo/_run_elementor_temp.py
|
||||
python -m demo.app
|
||||
```
|
||||
|
||||
## Verificar que quedo arriba
|
||||
@@ -27,12 +27,12 @@ Si responde `StatusCode: 200`, esta listo.
|
||||
Desde `c:\word`:
|
||||
|
||||
```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
|
||||
```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
|
||||
|
||||
@@ -4,6 +4,19 @@
|
||||
|
||||
## 🔄 Historial de Versiones
|
||||
|
||||
### Hash: `gkachele-elementor-restaurante-20260221-v22`
|
||||
**Fecha:** 21 Febrero 2026
|
||||
**Estado:** OK funcional (mejorable)
|
||||
|
||||
#### Cambios:
|
||||
- OK Drawer Pro estabilizado: apertura/cierre, overlay, z-index, lock scroll, Escape/foco y sync por viewport.
|
||||
- OK Reset de builder corrige limpieza de bloques + settings.
|
||||
- OK Se evita autoload de plantilla cuando ya hay estado guardado.
|
||||
- OK Tema restaurante sin colores duros por bloque; vuelve a depender de variables editables.
|
||||
- OK Se restaura comportamiento de ancho/contenedor para no romper bloques al 100%.
|
||||
- Pendiente: QA manual de cada icono del menu en preview final (plan siguiente lote).
|
||||
|
||||
---
|
||||
### Hash: `gkachele-elementor-apple-20260209-v14`
|
||||
**Fecha:** 09 Febrero 2026
|
||||
**Estado:** OK Fase 1
|
||||
@@ -238,6 +251,8 @@
|
||||
|
||||
**© 2025 GKACHELE™. Todos los derechos reservados.**
|
||||
|
||||
## 🔄 Historial de Versiones
|
||||
|
||||
### Hash: `gkachele-builder-docs-20260208-v1`
|
||||
**Fecha:** 08 Febrero 2026
|
||||
**Estado:** ✅ Documentado
|
||||
@@ -259,3 +274,6 @@
|
||||
- `memoria/ESTADO_ACTUAL.md` marcado como historico para evitar decisiones con contexto 2025.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +1,80 @@
|
||||
# Memoria Codex - GKACHELE
|
||||
|
||||
**Fecha de corte:** 14 Febrero 2026
|
||||
**Fecha de corte:** 24 Febrero 2026
|
||||
|
||||
## Fuente de verdad (estado IA)
|
||||
## Fuente de verdad
|
||||
- `codex/VERSIONADO_IA.md`
|
||||
- `codex/BUILDER_ELEMENTOR_VERSION.md`
|
||||
- `codex/HISTORIAL_CAMBIOS.md`
|
||||
- `codex/MEMORIA_CODEX.md`
|
||||
- `STATE.md` (raiz del workspace, memoria operativa Docker/SaaS desde 2026-02-28)
|
||||
|
||||
## Punto exacto
|
||||
## Estado acordado (retomar desde aqui)
|
||||
- Rama activa: `ai/ub24-builder-v1`
|
||||
- Commit HEAD: `6d696c4`
|
||||
- Version base builder: `gkachele-elementor-templates-20260210-v21`
|
||||
- Estado de sync local/remoto al corte: `0/0`
|
||||
- Checkpoint actual: `208dca9`
|
||||
- Tag local de checkpoint: `builder-social-blockfx-20260224-v1`
|
||||
- Base funcional estable previa: `e83e915` (tag local: `builder-stable-e83e915`)
|
||||
|
||||
## Estado actual funcional
|
||||
- Builder visual operativo en `/elementor/<site_id>`.
|
||||
- Drag/drop y resize fluido en canvas.
|
||||
- Inspector para estilos y contenido.
|
||||
- Modo libre con snapping.
|
||||
- Preview limpio y menu por modos (horizontal/acordeon/ambos).
|
||||
## Estado funcional verificado
|
||||
- Builder operativo en `/elementor/<site_id>`.
|
||||
- Customizer unificado en una sola ruta:
|
||||
- `/customizer/<id>` activo
|
||||
- `/customizer2/<id>` y `/customizer3/<id>` fuera (`404`)
|
||||
- Mejoras ya hechas en builder:
|
||||
- menu manual con mejor mapeo a bloques reales
|
||||
- drop en modo libre mas preciso
|
||||
- bloque `Redes` con mas controles visuales
|
||||
- controles de fondo/transicion por bloque (version inicial)
|
||||
|
||||
## Pendientes inmediatos
|
||||
- Cargar bloques reales por plantilla de rubro (no solo look-and-feel).
|
||||
- Definir persistencia de resenas al publicar.
|
||||
- Integrar calendario real (Calendly/Google).
|
||||
- Consolidar puente Builder -> SaaS (`/customizer/<id>`).
|
||||
## Feedback del usuario memorizado (obligatorio)
|
||||
- Hay cosas buenas en lo hecho.
|
||||
- El resultado visual actual todavia se percibe "cutre".
|
||||
- Las animaciones actuales no alcanzan nivel pro.
|
||||
- Los fondos (sobre todo imagen de fondo) no se ven profesionales.
|
||||
- Lo de `Redes` debe escalarse y aplicarse mejor a cada bloque.
|
||||
- No perder este contexto ni volver a "no saber en que quedamos".
|
||||
- Estandar visual confirmado por usuario: nivel premium como landing `campos-misiones`
|
||||
- Hero con fondo potente y composicion pro.
|
||||
- Bloques de contacto/redes con look comercial real.
|
||||
- Mapas embebidos y galerias con acabado profesional.
|
||||
- Botones con micro-movimiento y sombra consistente.
|
||||
- Este nivel debe replicarse en plantillas y en el customizer de `word`.
|
||||
|
||||
## Flujo operativo memorizado
|
||||
1. Construir y validar local en `http://127.0.0.1:5001`.
|
||||
2. Guardar cambios en commits atomicos.
|
||||
3. Push a `origin/ai/ub24-builder-v1` en cada lote validado.
|
||||
4. Actualizar hash y estado en `codex/VERSIONADO_IA.md`.
|
||||
## Siguiente lote acordado
|
||||
1. Subir calidad visual global (no solo `Redes`) con sistema consistente por bloque.
|
||||
2. Rehacer fondos animados a nivel pro (capas, movimiento elegante, control fino).
|
||||
3. Rehacer transiciones/hover con calidad premium y coherencia global.
|
||||
4. Mantener flujo: `1 archivo + 1 cambio + 1 prueba` por iteracion.
|
||||
|
||||
## Comando de arranque
|
||||
- `python -m demo.app` desde `c:\word`
|
||||
## Metodo operativo
|
||||
- Misma rama + control por hash/revert.
|
||||
- Sin push/tag sin orden explicita del usuario.
|
||||
- Validacion inmediata por URL canonica:
|
||||
- `http://127.0.0.1:5001/elementor/1`
|
||||
- Arranque:
|
||||
- `python -m demo.app` desde `c:\word`
|
||||
|
||||
## Memoria integrada desde STATE.md (raiz)
|
||||
- Objetivo paralelo vigente: automatizacion Docker estable en `C:\word` con flujo reproducible.
|
||||
- Regla activa: un solo flujo oficial de automatizacion, cambios pequenos y verificables, registro de fallo+fix.
|
||||
- Flujo Docker objetivo:
|
||||
1. `docker compose build`
|
||||
2. `docker compose up -d`
|
||||
3. `docker compose ps`
|
||||
4. `docker compose logs --tail=200`
|
||||
5. `docker compose restart <service>` cuando aplique
|
||||
- Proximo paso operativo en esa linea: definir compose canonico unico y retirar variantes no usadas.
|
||||
- Estandar visual SaaS validado (2026-02-28): referencia `Campos Misiones` como base premium replicable.
|
||||
|
||||
## Pendientes criticos memorizados (24 Febrero 2026)
|
||||
1. Paridad real entre editor y preview final:
|
||||
- lo que se ve mientras se edita debe verse igual en `/elementor/<id>/preview-final`.
|
||||
2. Movimiento libre real de objetos:
|
||||
- drag/reordenamiento debe funcionar estable y predecible.
|
||||
3. Cache:
|
||||
- revisar y corregir cache agresiva que oculta cambios recientes.
|
||||
4. Ancho de bloques:
|
||||
- no forzar 100% por defecto.
|
||||
- permitir control real de ancho (mas angosto/mas ancho) sin romper layout.
|
||||
5. Hero izquierda/derecha:
|
||||
- swap de contenido debe reflejarse claramente en editor y preview final.
|
||||
6. Modularizacion tecnica:
|
||||
- dividir `elementor_builder.html` en archivos mas pequenos (CSS/JS por responsabilidades) para evitar regressiones y acelerar fixes.
|
||||
|
||||
@@ -1,105 +1,127 @@
|
||||
# Versionado IA - UB24/Elementor
|
||||
# Versionado IA - UB24 / Elementor
|
||||
|
||||
## Rama de trabajo
|
||||
- `ai/ub24-builder-v1`
|
||||
## 0) Protocolo Bloqueante (siempre)
|
||||
1. Definir objetivo del lote en 1 linea.
|
||||
2. Tocar un solo archivo por fix (salvo excepcion aprobada).
|
||||
3. Hacer un solo cambio por intento.
|
||||
4. Probar inmediatamente con flujo fijo acordado.
|
||||
5. Si falla, revert inmediato por hash/commit (sin encadenar parches).
|
||||
6. Si pasa, commit atomico con mensaje semantico.
|
||||
7. Push solo con orden explicita del usuario.
|
||||
8. Antes de declarar estado, verificar sync real (`fetch` + `rev-list`).
|
||||
|
||||
## Estado de sincronizacion (14 Febrero 2026)
|
||||
- Local: `ai/ub24-builder-v1`
|
||||
- Remoto: `origin/ai/ub24-builder-v1`
|
||||
- Divergencia verificada: `0/0` (sin commits pendientes entre local y remoto)
|
||||
## 1) Objetivo
|
||||
Definir un proceso de versionado auditable, reproducible y estable para el desarrollo del builder UB24.
|
||||
|
||||
## Regla de trabajo
|
||||
1. Cada cambio funcional se guarda en un commit separado.
|
||||
2. Cada commit se registra con su hash.
|
||||
3. Cada commit debe incluir comando de reversion rapida.
|
||||
4. La rama debe quedar sincronizada con remoto al cerrar bloque de trabajo.
|
||||
## 2) Estado actual verificado
|
||||
- Fecha de verificacion: `2026-02-22`
|
||||
- Rama activa: `ai/ub24-builder-v1`
|
||||
- Upstream: `origin/ai/ub24-builder-v1`
|
||||
- Divergencia local/remoto: `3 0`
|
||||
- Comando usado:
|
||||
- `git rev-list --left-right --count ai/ub24-builder-v1...origin/ai/ub24-builder-v1`
|
||||
|
||||
## Convencion de mensaje
|
||||
## 3) Politica de versionado
|
||||
1. Commits atomicos por cambio funcional.
|
||||
2. Mensajes bajo convencion semantica.
|
||||
3. Reversion siempre definida por commit.
|
||||
4. Cada bloque validado se empuja a remoto.
|
||||
5. No mezclar cambios de infraestructura con cambios de UX en el mismo commit.
|
||||
|
||||
## 4) Convencion de commits
|
||||
- `feat(builder): ...`
|
||||
- `fix(builder): ...`
|
||||
- `refactor(builder): ...`
|
||||
- `chore(versioning): ...`
|
||||
- `docs(builder): ...`
|
||||
|
||||
## Flujo con Gitea
|
||||
1. Trabajo local en `ai/ub24-builder-v1`.
|
||||
2. Push continuo a `origin/ai/ub24-builder-v1`.
|
||||
3. Merge cuando validemos en local y Raspberry.
|
||||
|
||||
## Protocolo fijo de sincronizacion (siempre)
|
||||
1. Verificar rama activa: `git branch --show-current`
|
||||
2. Actualizar referencias remotas: `git fetch origin --prune`
|
||||
3. Medir divergencia: `git rev-list --left-right --count ai/ub24-builder-v1...origin/ai/ub24-builder-v1`
|
||||
4. Si el resultado no es `0 0`, sincronizar antes de continuar.
|
||||
5. Despues de cada lote validado:
|
||||
## 5) Flujo obligatorio por sesion
|
||||
1. Verificar rama activa:
|
||||
- `git branch --show-current`
|
||||
2. Actualizar referencias remotas:
|
||||
- `git fetch origin --prune`
|
||||
3. Verificar divergencia:
|
||||
- `git rev-list --left-right --count ai/ub24-builder-v1...origin/ai/ub24-builder-v1`
|
||||
4. Si no es `0 0`, sincronizar antes de editar.
|
||||
5. Al cerrar lote validado:
|
||||
- `git add <archivos>`
|
||||
- `git commit -m "tipo(scope): mensaje"`
|
||||
- `git push origin ai/ub24-builder-v1`
|
||||
6. Registrar hash y objetivo en este archivo y en `codex/HISTORIAL_CAMBIOS.md`.
|
||||
|
||||
## Registro de hashes
|
||||
### Baseline
|
||||
- Commit: `cb99f26`
|
||||
- Objetivo: crear rama y politica de versionado para trabajo IA.
|
||||
- Revert:
|
||||
- `git revert <hash>`
|
||||
- `git reset --hard <hash_anterior>` (solo con aprobacion explicita)
|
||||
## 5.1) Metodo de ejecucion memorizado (obligatorio)
|
||||
1. Misma rama activa (`ai/ub24-builder-v1`), sin ramas paralelas para fixes rapidos.
|
||||
2. Control por hash/revert: si un intento falla, revert inmediato por commit.
|
||||
3. Un archivo por fix cuando sea posible (evitar mezclar cambios laterales).
|
||||
4. Validacion funcional inmediata despues de cada microcambio.
|
||||
5. No commit/push sin orden explicita del usuario.
|
||||
|
||||
### Correccion historial
|
||||
- Commit: `fe8657e`
|
||||
- Objetivo: revertir commit no deseado y mantener separacion de cambios.
|
||||
- Revert:
|
||||
- `git revert fe8657e`
|
||||
|
||||
### Fix local Elementor
|
||||
- Commit: `22e564e`
|
||||
- Objetivo: robustecer arranque local y carga de themes en Windows (BOM + logs seguros).
|
||||
- Revert:
|
||||
- `git revert 22e564e`
|
||||
|
||||
### Fase 1 Builder (visual pro)
|
||||
- Commit: `1c04f04`
|
||||
- Objetivo: consolidar estilos reutilizables y subir calidad visual en hero, features, cards y contact del preview.
|
||||
- Revert:
|
||||
- `git revert 1c04f04`
|
||||
|
||||
### Ajustes Builder (limpieza + preview + ancho)
|
||||
- Commit: `7c5f671`
|
||||
- Objetivo: quitar texto en barra Apple, limpiar menu vacio, preview local funcional sin salir de builder, ancho desktop al 100%, control de ancho por bloque y descripcion en bloque video.
|
||||
- Revert:
|
||||
- `git revert 7c5f671`
|
||||
|
||||
### Ajustes Builder (preview limpio + menu modos)
|
||||
- Commit: `dd98e9d`
|
||||
- Objetivo: mejorar vista previa (forzar modo limpio y restaurar estado), eliminar precarga automatica de bloques, y agregar modo de menu (horizontal/acordeon/ambos).
|
||||
- Revert:
|
||||
- `git revert dd98e9d`
|
||||
|
||||
## URL local canonica (unificada)
|
||||
- Base local: `http://127.0.0.1:5001`
|
||||
- Builder local: `http://127.0.0.1:5001/elementor/1`
|
||||
- Regla: usar siempre `127.0.0.1` (no `localhost`) en scripts, pruebas y documentacion local.
|
||||
|
||||
## Arranque rapido local (Windows)
|
||||
1. Desde `c:\word`, ejecutar:
|
||||
- `python demo/_run_elementor_temp.py`
|
||||
2. Abrir:
|
||||
## 6) Protocolo de arranque local
|
||||
- Comando canonico:
|
||||
- `python -m demo.app`
|
||||
- URL canonica:
|
||||
- `http://127.0.0.1:5001`
|
||||
- Builder:
|
||||
- `http://127.0.0.1:5001/elementor/1`
|
||||
3. Verificacion rapida:
|
||||
- Verificacion rapida:
|
||||
- `Invoke-WebRequest http://127.0.0.1:5001/elementor/1 -UseBasicParsing`
|
||||
|
||||
Notas:
|
||||
- En el primer arranque puede tardar ~40-50 segundos antes de quedar escuchando en `5001`.
|
||||
- Logs:
|
||||
- `c:\word\logs_demo_app.txt`
|
||||
- `c:\word\logs_demo_app.err`
|
||||
## 7) Criterios de estabilidad (no negociables)
|
||||
1. No tocar funcionalidades estables sin requerimiento explicito.
|
||||
2. Mantener una sola estrategia de layout y DnD por flujo.
|
||||
3. Mantener una sola ruta de guardado activa:
|
||||
- `/api/elementor/save`
|
||||
4. Validar guardado/publicacion antes de cerrar lote.
|
||||
5. Evitar cambios amplios si un fix local resuelve el problema.
|
||||
|
||||
## Control de rama (local/remoto)
|
||||
- Rama local activa: `ai/ub24-builder-v1`
|
||||
- Upstream remoto: `origin/ai/ub24-builder-v1`
|
||||
- Estado al registrar: `en sync (0/0)` al 14 Febrero 2026
|
||||
- Politica: commits atomicos + push por lote validado + verificacion de divergencia al inicio y al cierre.
|
||||
## 8) Registro de commits relevantes
|
||||
| Fecha | Commit | Tipo | Objetivo | Revert recomendado |
|
||||
|---|---|---|---|---|
|
||||
| 2026-02-21 | `e83e915` | fix | Estabilizar Drawer Pro, reset real de builder y restaurar tema restaurante editable + ancho | `git revert e83e915` |
|
||||
| 2026-02-14 | `cb99f26` | chore | Crear rama y politica inicial de versionado IA | `git revert cb99f26` |
|
||||
| 2026-02-14 | `fe8657e` | fix | Revertir commit no deseado y limpiar historial | `git revert fe8657e` |
|
||||
| 2026-02-14 | `22e564e` | fix | Robustecer arranque local/theme load en Windows | `git revert 22e564e` |
|
||||
| 2026-02-14 | `1c04f04` | feat | Mejora visual base (hero/features/cards/contact) | `git revert 1c04f04` |
|
||||
| 2026-02-14 | `7c5f671` | fix | Limpieza preview/menu y ajustes de ancho | `git revert 7c5f671` |
|
||||
| 2026-02-14 | `dd98e9d` | fix | Preview limpio y menu por modos | `git revert dd98e9d` |
|
||||
| 2026-02-15 | `1a5778b` | refactor | Runtime unificado `python -m demo.app` + blueprint Elementor | `git revert 1a5778b` |
|
||||
| 2026-02-15 | `8ac360b` | feat | Educacion V2 + correccion de anclas | `git revert 8ac360b` |
|
||||
| 2026-02-15 | `f6d8ab1` | fix | Corregir wrapper SQLite para evitar fallo de arranque | `git revert f6d8ab1` |
|
||||
| 2026-02-15 | `b6fb4da` | feat | API dedicada `/api/elementor/save` con opcion publicar | `git revert b6fb4da` |
|
||||
| 2026-02-15 | `c2ee81d` | fix | Persistencia de bloques + feedback de publish | `git revert c2ee81d` |
|
||||
| 2026-02-15 | `e20f086` | fix | Preview full-page y layout estable por secciones | `git revert e20f086` |
|
||||
| 2026-02-15 | `e5df6de` | feat | Full width + soporte real de 2 columnas | `git revert e5df6de` |
|
||||
| 2026-02-15 | `a6089ee` | feat | Drag inteligente por drop + preview en nueva pestana | `git revert a6089ee` |
|
||||
| 2026-02-15 | `f9f7d23` | fix | Quitar layout global y forzar preview completo | `git revert f9f7d23` |
|
||||
| 2026-02-15 | `f363eef` | fix | Correccion de texto/UI + modo libre + `?full=1` | `git revert f363eef` |
|
||||
| 2026-02-16 | `6f14308` | fix | Unificar layout y estabilizar redimension en canvas | `git revert 6f14308` |
|
||||
|
||||
## 9) Decisiones funcionales vigentes
|
||||
- Rubros oficiales permitidos:
|
||||
- `restaurante`
|
||||
- `danza`
|
||||
- `cosmeticos`
|
||||
- `despachos`
|
||||
- `gimnasios`
|
||||
- `educacion`
|
||||
- `base_otro`
|
||||
- Preview final separada habilitada:
|
||||
- `GET /elementor/<site_id>/preview-final`
|
||||
- `GET /ub24/<site_id>/preview-final`
|
||||
- Motor de reordenamiento seleccionado para DnD:
|
||||
- `SortableJS` (estrategia unica)
|
||||
|
||||
## 10) Pendientes priorizados
|
||||
1. QA manual de cada icono/accion del menu superior en preview final (siguiente lote).
|
||||
2. Footer global obligatorio con autoria del proyecto.
|
||||
3. Watermark de autoria en codigo bajo convencion unica.
|
||||
4. Flujo dual estable de preview (editor/pagina real).
|
||||
5. Mejora de interaccion touch/capacitiva del builder.
|
||||
|
||||
## 11) Referencias cruzadas
|
||||
- Historial funcional detallado: `codex/HISTORIAL_CAMBIOS.md`
|
||||
- Arranque rapido local: `codex/ARRANQUE_RAPIDO_UB24.md`
|
||||
- Flujo general del proyecto: `codex/FLUJO_PROYECTO.md`
|
||||
|
||||
## 12) Nota operativa
|
||||
Este archivo define el estandar de trabajo. Cualquier cambio de proceso debe registrarse en un commit `chore(versioning)` y quedar reflejado aqui.
|
||||
|
||||
## Fases memorizadas (builder)
|
||||
1. Fase 1 (UI Pro base): navbar premium, hero premium, sistema de espaciado/grid, pulido visual consistente.
|
||||
2. Fase 2 (estructura): separar renderers por bloque y reducir inline styles para automatizacion.
|
||||
3. Fase 3 (presets): presets por rubro + reglas responsive + variantes exportables.
|
||||
|
||||
48
demo/app.py
48
demo/app.py
@@ -1,17 +1,37 @@
|
||||
"""
|
||||
GKACHELE™ SaaS PageBuilder - Sistema Modular
|
||||
© 2025 GKACHELE™. Todos los derechos reservados.
|
||||
"""
|
||||
GKACHELE SaaS PageBuilder - Sistema Modular
|
||||
"""
|
||||
|
||||
import os
|
||||
from flask import Flask, jsonify, request
|
||||
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
|
||||
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 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
|
||||
|
||||
try:
|
||||
from elementor.routes import elementor_bp
|
||||
except ImportError:
|
||||
elementor_bp = None
|
||||
|
||||
app = Flask(__name__, template_folder='templates', static_folder='static')
|
||||
app.secret_key = SECRET_KEY
|
||||
@@ -26,6 +46,8 @@ app.register_blueprint(dashboard_bp)
|
||||
app.register_blueprint(customizer_bp)
|
||||
app.register_blueprint(admin_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
|
||||
@app.errorhandler(500)
|
||||
@@ -34,15 +56,18 @@ def handle_500(e):
|
||||
response.status_code = 500
|
||||
return response
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def handle_404(e):
|
||||
return jsonify({'success': False, 'error': 'No encontrado'}), 404
|
||||
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def handle_exception(e):
|
||||
print(f"ERROR: EXCEPCION: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# Middleware
|
||||
@app.after_request
|
||||
def add_header(response):
|
||||
@@ -50,6 +75,7 @@ def add_header(response):
|
||||
response.headers['Content-Security-Policy'] = "frame-ancestors *;"
|
||||
return response
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(f"GKACHELE SaaS Modular iniciado en puerto {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
|
||||
128
elementor/routes.py
Normal file
128
elementor/routes.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from flask import Blueprint, render_template, session, request, jsonify, make_response
|
||||
import json
|
||||
from db import get_db
|
||||
from utils.theme_engine import get_theme_config
|
||||
|
||||
|
||||
elementor_bp = Blueprint(
|
||||
'elementor',
|
||||
__name__,
|
||||
template_folder='templates',
|
||||
static_folder='static',
|
||||
static_url_path='/elementor/static'
|
||||
)
|
||||
|
||||
|
||||
def _render_builder(site_id, builder_mode='default', preview_only=False, **_kwargs):
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT user_id, slug, theme, content_json FROM sites WHERE id = ?', (site_id,))
|
||||
site = c.fetchone()
|
||||
|
||||
if not site:
|
||||
conn.close()
|
||||
return 'Sitio no encontrado', 404
|
||||
|
||||
if 'user_id' in session and site[0] != session['user_id']:
|
||||
conn.close()
|
||||
return 'No autorizado', 403
|
||||
|
||||
content = json.loads(site[3]) if site[3] else {}
|
||||
if not isinstance(content, dict):
|
||||
content = {}
|
||||
|
||||
theme = site[2]
|
||||
theme_config = get_theme_config(theme)
|
||||
|
||||
c.execute('SELECT plan, rubro FROM users WHERE id = ?', (site[0],))
|
||||
user_data = c.fetchone()
|
||||
conn.close()
|
||||
user_plan = user_data[0] if user_data else 'base'
|
||||
user_rubro = user_data[1] if user_data else 'restaurante'
|
||||
|
||||
html = render_template(
|
||||
'elementor_builder.html',
|
||||
site_id=site_id,
|
||||
slug=site[1],
|
||||
theme=theme,
|
||||
content=content,
|
||||
theme_config=theme_config,
|
||||
user_plan=user_plan,
|
||||
rubro=user_rubro,
|
||||
builder_mode=builder_mode,
|
||||
preview_only=bool(preview_only)
|
||||
)
|
||||
response = make_response(html)
|
||||
# Dynamic builder output should never be cached.
|
||||
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
response.headers['Expires'] = '0'
|
||||
return response
|
||||
|
||||
|
||||
@elementor_bp.route('/elementor/<int:site_id>')
|
||||
def elementor_view(site_id):
|
||||
return _render_builder(site_id, builder_mode='default')
|
||||
|
||||
|
||||
@elementor_bp.route('/elementor/<int:site_id>/preview-final')
|
||||
def elementor_preview_final(site_id):
|
||||
return _render_builder(site_id, builder_mode='default', preview_only=True)
|
||||
|
||||
|
||||
@elementor_bp.route('/ub24/<int:site_id>')
|
||||
def ub24_view(site_id):
|
||||
return _render_builder(site_id, builder_mode='ub24')
|
||||
|
||||
|
||||
@elementor_bp.route('/ub24/<int:site_id>/preview-final')
|
||||
def ub24_preview_final(site_id):
|
||||
return _render_builder(site_id, builder_mode='ub24', preview_only=True)
|
||||
|
||||
|
||||
@elementor_bp.route('/api/elementor/save', methods=['POST'])
|
||||
def save_elementor():
|
||||
data = request.get_json(silent=True) or {}
|
||||
site_id = data.get('site_id')
|
||||
content = data.get('content')
|
||||
publish = bool(data.get('publish'))
|
||||
|
||||
if not site_id or not isinstance(content, dict):
|
||||
return jsonify({'success': False, 'error': 'Payload invalido'}), 400
|
||||
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT user_id, content_json FROM sites WHERE id = ?', (site_id,))
|
||||
row = c.fetchone()
|
||||
if not row:
|
||||
conn.close()
|
||||
return jsonify({'success': False, 'error': 'Sitio no encontrado'}), 404
|
||||
|
||||
owner_id = row[0]
|
||||
if 'user_id' in session and session['user_id'] != owner_id:
|
||||
conn.close()
|
||||
return jsonify({'success': False, 'error': 'No autorizado'}), 403
|
||||
|
||||
current_content = {}
|
||||
try:
|
||||
if row[1]:
|
||||
current_content = json.loads(row[1]) or {}
|
||||
except Exception:
|
||||
current_content = {}
|
||||
|
||||
merged = dict(current_content)
|
||||
merged.update(content)
|
||||
|
||||
if publish:
|
||||
c.execute('UPDATE sites SET content_json = ?, status = ? WHERE id = ?', (json.dumps(merged), 'published', site_id))
|
||||
else:
|
||||
c.execute('UPDATE sites SET content_json = ? WHERE id = ?', (json.dumps(merged), site_id))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
response = jsonify({'success': True, 'published': publish})
|
||||
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
response.headers['Expires'] = '0'
|
||||
return response
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user