From 59812e547e3d1b3d848977e89eb66f9601e23e31 Mon Sep 17 00:00:00 2001 From: komkida91 Date: Sat, 31 Jan 2026 16:04:55 +0100 Subject: [PATCH] feat: Add Dockerfile and initial Docker setup files --- MEMORIA_PROYECTO.md | 33 + README.md | 173 +- demo/Dockerfile | 25 +- demo/__pycache__/config.cpython-314.pyc | Bin 0 -> 1205 bytes demo/__pycache__/database.cpython-314.pyc | Bin 0 -> 6410 bytes demo/customizer.html | 188 ++ demo/database.py | 4 +- demo/database/main.db | Bin 28672 -> 53248 bytes demo/requirements.txt | 6 + .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 130 bytes demo/routes/__pycache__/admin.cpython-314.pyc | Bin 0 -> 4798 bytes demo/routes/__pycache__/auth.cpython-314.pyc | Bin 0 -> 7265 bytes .../__pycache__/customizer.cpython-314.pyc | Bin 0 -> 10217 bytes .../__pycache__/dashboard.cpython-314.pyc | Bin 0 -> 4751 bytes .../routes/__pycache__/public.cpython-314.pyc | Bin 0 -> 2078 bytes demo/routes/customizer.py | 60 +- demo/routes/get_blocks_fix.py | 25 + demo/static/test.html | 13 + demo/templates/admin.html | 83 +- demo/templates/customizer.html | 916 ++++----- demo/templates/customizer.html.bak | 1704 +++++++++++++++++ demo/templates/customizer.html.v2.bak | 1704 +++++++++++++++++ demo/templates/customizer_pro_ready.html | 1704 +++++++++++++++++ demo/templates/demo_pro_v3.html | 597 ++++++ demo/themes/restaurante-moderno/template.html | 1610 +++++----------- .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 129 bytes .../auth_decorators.cpython-314.pyc | Bin 0 -> 2197 bytes .../__pycache__/theme_engine.cpython-314.pyc | Bin 0 -> 11546 bytes demo/utils/theme_engine.py | 9 + docker-compose.yml | 22 - saas-demo.html | 620 ++++++ 31 files changed, 7720 insertions(+), 1776 deletions(-) create mode 100644 MEMORIA_PROYECTO.md create mode 100644 demo/__pycache__/config.cpython-314.pyc create mode 100644 demo/__pycache__/database.cpython-314.pyc create mode 100644 demo/customizer.html create mode 100644 demo/routes/__pycache__/__init__.cpython-314.pyc create mode 100644 demo/routes/__pycache__/admin.cpython-314.pyc create mode 100644 demo/routes/__pycache__/auth.cpython-314.pyc create mode 100644 demo/routes/__pycache__/customizer.cpython-314.pyc create mode 100644 demo/routes/__pycache__/dashboard.cpython-314.pyc create mode 100644 demo/routes/__pycache__/public.cpython-314.pyc create mode 100644 demo/routes/get_blocks_fix.py create mode 100644 demo/static/test.html create mode 100644 demo/templates/customizer.html.bak create mode 100644 demo/templates/customizer.html.v2.bak create mode 100644 demo/templates/customizer_pro_ready.html create mode 100644 demo/templates/demo_pro_v3.html create mode 100644 demo/utils/__pycache__/__init__.cpython-314.pyc create mode 100644 demo/utils/__pycache__/auth_decorators.cpython-314.pyc create mode 100644 demo/utils/__pycache__/theme_engine.cpython-314.pyc delete mode 100644 docker-compose.yml create mode 100644 saas-demo.html diff --git a/MEMORIA_PROYECTO.md b/MEMORIA_PROYECTO.md new file mode 100644 index 0000000..bb97a0e --- /dev/null +++ b/MEMORIA_PROYECTO.md @@ -0,0 +1,33 @@ +# Memoria del Proyecto: GKACHELE™ SaaS + +Este documento sirve como un resumen contextual para la IA y el desarrollador. + +## 1. Visión General del Proyecto + +* **Nombre:** GKACHELE™ +* **Tipo:** SaaS (Software as a Service) para la creación de sitios web. +* **Concepto:** Un "WordPress" propio, auto-alojado y hecho a medida. + +## 2. Arquitectura y Tecnología + +* **Backend:** Aplicación monolítica desarrollada en **Python** con el microframework **Flask**. +* **Código Principal:** Ubicado en el directorio `demo/`. +* **Entrypoint:** `demo/app.py`. +* **Base de Datos Actual:** SQLite, en el archivo `demo/database/main.db`. +* **Motor de Plantillas:** Un sistema personalizado (`demo/utils/theme_engine.py`) que imita la lógica de temas de WordPress. + +## 3. Despliegue y Operaciones (DevOps) + +* **Entorno de Producción:** Una **Raspberry Pi**. +* **Proceso de Despliegue Actual:** Manual, mediante scripts (`.sh`) que copian archivos vía `scp` y gestionan el servicio con `systemd`. +* **Control de Versiones:** **Gitea**, autohospedado. + +## 4. Objetivo Estratégico Actual + +El objetivo principal es **modificar y modernizar la aplicación existente de forma incremental**, no reescribirla desde cero. + +El plan de acción es el siguiente: + +1. **Contenerización:** Empaquetar la aplicación Flask y sus servicios en contenedores **Docker**. +2. **Migración de Base de Datos:** Reemplazar SQLite por **PostgreSQL**, ejecutándose en su propio contenedor Docker. +3. **Automatización (CI/CD):** Configurar **Gitea Actions** para automatizar el proceso de construcción de imágenes Docker y el despliegue en la Raspberry Pi tras cada `git push`. diff --git a/README.md b/README.md index 5244b98..8f380cc 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,75 @@ -# WordPress con Docker Compose - -Ejemplo básico de WordPress usando Docker Compose con MySQL y phpMyAdmin. - -## 🚀 Inicio Rápido - -### Requisitos -- Docker instalado -- Docker Compose instalado - -### Instalación - -1. **Clonar o descargar este proyecto** - -2. **Iniciar los contenedores:** -```bash -docker-compose up -d -``` - -3. **Acceder a WordPress:** - - Abre tu navegador en: http://localhost:8080 - - Sigue el asistente de instalación de WordPress - -4. **Acceder a phpMyAdmin (opcional):** - - Abre tu navegador en: http://localhost:8081 - - Usuario: `root` - - Contraseña: `root_password` - -## 📝 Configuración - -### Cambiar puertos -Si los puertos 8080 o 8081 están ocupados, edita `docker-compose.yml`: -```yaml -ports: - - "TU_PUERTO:80" # Cambia TU_PUERTO por el que prefieras -``` - -### Cambiar credenciales -Edita las variables de entorno en `docker-compose.yml`: -- `MYSQL_PASSWORD`: Contraseña del usuario de WordPress -- `MYSQL_ROOT_PASSWORD`: Contraseña del root de MySQL -- `WORDPRESS_DB_PASSWORD`: Debe coincidir con `MYSQL_PASSWORD` - -## 🛠️ Comandos Útiles - -### Ver logs -```bash -docker-compose logs -f -``` - -### Detener contenedores -```bash -docker-compose down -``` - -### Detener y eliminar volúmenes (⚠️ borra los datos) -```bash -docker-compose down -v -``` - -### Reiniciar un servicio específico -```bash -docker-compose restart wordpress -``` - -### Ver contenedores en ejecución -```bash -docker-compose ps -``` - -## 📁 Estructura - -``` -. -├── docker-compose.yml # Configuración de servicios -├── wp-content/ # Temas y plugins personalizados (se crea automáticamente) -└── README.md # Este archivo -``` - -## 🔒 Seguridad - -⚠️ **IMPORTANTE**: Este es un ejemplo básico para desarrollo. Para producción: -- Cambia todas las contraseñas por defecto -- Usa variables de entorno seguras -- Configura SSL/TLS -- Implementa un firewall -- Usa secrets de Docker o un gestor de secretos - -## 🐳 Servicios Incluidos - -- **WordPress**: Aplicación principal (puerto 8080) -- **MySQL 8.0**: Base de datos (puerto interno) -- **phpMyAdmin**: Administrador de base de datos (puerto 8081) - -## 📚 Recursos - -- [Documentación de WordPress](https://wordpress.org/support/) -- [Documentación de Docker Compose](https://docs.docker.com/compose/) +# GKACHELE™ Agent - Configuración del Asistente y Documentación del Proyecto + +Este archivo define las reglas, el contexto y el flujo principal para el asistente de IA (y cualquier desarrollador) trabajando en el ecosistema GKACHELE™. + +## 🎯 Misión del Proyecto +GKACHELE™ es un sistema SaaS modular para la creación y gestión de sitios web, enfocado en la flexibilidad, el control de versiones mediante Gitea y el despliegue optimizado en Raspberry Pi/Linux. + +## ⚠️ REGLAS CRÍTICAS (NUNCA ROMPER) + +### 1. Prohibición de Referencias Externas +- **NUNCA** mencionar "WordPress", "wordpress", "WP" o "wp-" en el código fuente. +- Este es un sistema **propio e independiente**. +- **Reemplazos**: + - "WordPress" -> "GKACHELE™" o "Sistema Modular". + - "wp-admin" -> "/dashboard". + - "wp_options" -> "tabla settings". + +### 2. Metodología GKACHELE™ +- **Prioridad 1: Funcionalidad**. Hacer que el código funcione y sea verificado en producción (Raspberry). +- **Prioridad 2: Limpieza**. Refactorizar y optimizar SOLO después de que la funcionalidad sea confirmada. +- **Flujo**: `Funcionalidad -> Probar -> Funciona -> Limpieza -> Documentar`. + +## 🛠️ Skills & Infraestructura + +### Gestión de Repositorios (Gitea) +- Uso de `gitea_connector.py` para automatizar la creación de organizaciones y repositorios por cliente. +- Workflows de auto-commit y auto-deploy tras cambios en el customizer. + +### Infraestructura (Raspberry Pi & Docker) +- Despliegue mediante `docker-compose`. +- Scripts de sincronización: `sync-to-raspberry.sh`, `update-code-pi.sh`. +- Dominios gestionados via DuckDNS. + +## 📝 Guías de Trabajo para el Agente +- **Análisis antes de actuar**: Siempre revisar la carpeta `memoria/` antes de realizar cambios estructurales. +- **Verificación de reglas**: Antes de cada commit, realizar un grep para asegurar que no se colaron referencias prohibidas. +- **Persistencia**: Actualizar `task.md` y la memoria del proyecto tras completar hitos importantes. + +--- + +## 🔄 Flujo Principal de la Aplicación GKACHELE™ + +Este es el proceso completo, desde un nuevo visitante hasta un sitio web publicado: + +1. **Visita a la Landing Page (`/`):** + * Un cliente potencial visita la página principal del servicio. + +2. **Solicitud de Plan y Configuración Inicial:** + * Desde la Landing Page, el cliente selecciona un plan (Base, Pro, Premium). + * Rellena un formulario emergente (menú desplegable) con información inicial (nombre, email, rubro, etc.). + * Este proceso redirige al usuario a la página de registro con los datos pre-cargados. + +3. **Registro de Cliente (`/register`):** + * El cliente finaliza su registro en la plataforma. + +4. **Creación del Sitio en Borrador (`/customizer`):** + * Una vez registrado y/o logueado, el cliente es dirigido al "Customizer". Aquí puede diseñar y personalizar su sitio web, que permanece en estado de borrador y no es público. + +5. **Envío de Solicitud de Publicación:** + * Cuando el cliente considera que su sitio en borrador está listo, lo envía para tu revisión y aprobación. + +6. **Revisión en el Dashboard del Administrador (`/dashboard`):** + * Tú, como administrador del sistema, recibes esta solicitud en tu panel principal. Aquí puedes ver todos los sitios pendientes de aprobación. + +7. **Aprobación o Rechazo Manual:** + * Revisas el sitio del cliente. Desde tu dashboard, decides si **apruebas** o **rechazas** la solicitud de publicación. + +8. **Panel de Cliente (`/admin`) y Publicación del Sitio:** + * Si la solicitud es **aprobada**, el sitio web del cliente se **publica**, y se habilita un panel de administración específico para ese cliente (`/admin`), desde donde puede gestionar su sitio en línea. + +9. **Visualización Pública del Sitio:** + * El sitio web aprobado y publicado es ahora accesible para cualquier usuario de internet en su dominio asignado. + +--- +**© 2025 GKACHELE™. Todos los derechos reservados.** \ No newline at end of file diff --git a/demo/Dockerfile b/demo/Dockerfile index d7199b6..d0590de 100644 --- a/demo/Dockerfile +++ b/demo/Dockerfile @@ -1,21 +1,20 @@ -FROM python:3.11-slim +# Usa una imagen oficial de Python como base +FROM python:3.9-slim-buster +# Establece el directorio de trabajo dentro del contenedor WORKDIR /app -# Instalar dependencias del sistema -RUN apt-get update && apt-get install -y \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Copiar requirements e instalar -COPY demo/requirements.txt . +# Copia el archivo de requisitos e instálalos +COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -# Copiar el resto del código +# Copia el resto de la aplicación al directorio de trabajo COPY . . -# Exponer el puerto -EXPOSE 5000 +# Expone el puerto en el que corre la aplicación Flask (definido en config.py) +EXPOSE 5001 -# Comando para arrancar -CMD ["python", "demo/app.py"] +# Comando para correr la aplicación +# Asegúrate de que app.py esté en el directorio raíz de WORKDIR (/app) +# Y que las variables de entorno si son necesarias para SECRET_KEY y PORT se pasen al docker run o compose +CMD ["python", "app.py"] diff --git a/demo/__pycache__/config.cpython-314.pyc b/demo/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9975899ff5e04bce49a70c1b3f2d765ac9f8d8fd GIT binary patch literal 1205 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 QayXdqZs08&bFRYoUnow}9{>OV literal 0 HcmV?d00001 diff --git a/demo/customizer.html b/demo/customizer.html new file mode 100644 index 0000000..c0f557a --- /dev/null +++ b/demo/customizer.html @@ -0,0 +1,188 @@ + + + + + + GKACHELE Customizer + + + + +
+ + +
+
+ Vista previa +
+ + +
+
+
+
+
+
+
+ + + + diff --git a/demo/database.py b/demo/database.py index 757aee1..59929f1 100644 --- a/demo/database.py +++ b/demo/database.py @@ -150,6 +150,6 @@ def init_db(): conn.commit() conn.close() - print("✅ Base de datos GKACHELE inicializada correctamente.") + print("Base de datos GKACHELE inicializada correctamente.") except Exception as e: - print(f"❌ Error inicializando DB: {e}") + print(f" Error inicializando DB: {e}") diff --git a/demo/database/main.db b/demo/database/main.db index e09825c5ceef6dcc0e7c2b5c1545e50a6ed4404f..1676475a05c8ea6101233403f8be4bb05ea79b22 100644 GIT binary patch literal 53248 zcmeI)?N8fA90zbG6eyI3YFZanRkb5jDyY!Kc0veJrBY@(RG>73ZfKjLi+uqXCr)h# zT6KNV(yrCs?cHAN|JeSHssF?_?d7Ds+}$M(amz!?R#DK`rLl9l^TX$NKA!{UhK!iCt&I{_^@Y|FORSl~Bj{WE!Ex?qsP z^o@CgG!w~4FKL@>i_9$+jGIQDEam4Grt_=hma$5vR|>c17J2yvW3ez6DN*;CS8OY` zc)LIrSLWxVxqQxfa4zaGN(#nTd=0y!w=`ST&GEqo25*C3e4R zu?MXK|J<0JUYReDR5Yu}cP?O7kynVq+=8)Om|j>SVcwaQe4f)3Tc1SB)@#pl3r}e;p@M(GB`u?H}f3v}E&Uzxj4;%9j>B8kR^Ptv;UjS(Fp5JVuqDaT(^02+! zEiVr*I-8dV%cd2^Urt9NB$JNj+}xvOFWU~Q@?G8UOci-*G*~|aqKE<_CT_?~cSaOl=EqTj%dB`iuwn z1e=H3>W{tI7xV)f)cr=FS}~}xs>LrVZBcl~%PjGo`kGwnP9P(3*tZk4N11(NCu1-= z+S!%3me})JDSB!ALMdil+ow*+6Y%+3<0>f<XJR zbDOtS!(KOW*X97P}i0SG_< z0uX=z1Rwwb2teQn1)faBdKLA_>V;wb@_A}i?CSNk4Qj44hg~(@O0BeEm9;5zjA;=|$tl15>?sDLtW=C(Gpwoy?ZZl%Cd7TFT6n&8)6v)Jap#FsrPp z>5@8`WfK|G(k4t(%QBWprD&=&VbwfVvFjC&f5ArU9_7E5C}{_ z%AK?Qz@vfdHEv0tWa7Pw{$%ysus(D?ppL`5gZDnS@?~bm&dlgrUpNca!}KM-^Vw9+ zN|!X<$}nBaS(B=+YdR}U>Z+w_lbN(;QB}{D$~mgCl9{tuI+aaj-oqx{JBPqoFe{5hw;Xjxl009U<00Izz00bZa0SG_<0{@4=_D6hJLXWN|o)76t z#U5|_`}%SIbcdk*eZ}QZE33Nl0SS+i;-42o6Hl`dMGS{j*ge-HZM#7%MrvN2 zl_+0w$dVZ5e}j(j$5_DbSsz5X0jqZU=ISBRWrrE@q8lMcOSmoHu91OCO6{xLxS0uX=z1Rwwb2tWV=5P$##AaG&@aQ}Z| zLyH5000bZa0SG_<0uX=z1Rwwb2($!n|KFm(0t6rc0SG_<0uX=z1Rwwb2teS(3gG_# z#D*3J1_1~_00Izz00bZa0SG_<0uX2ki0A()82{h$zybsy009U<00Izz00bZa0SG|g zBnsg7|0gl3I3@@{00Izz00bZa0SG_<0uX>eQ~>w?Q4hR>00bZa0SG_<0uX=z1Rwwb J2%JQLe*w3pYzzPZ delta 152 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 Nih9h#Gd>P${3nZn#;gDU literal 0 HcmV?d00001 diff --git a/demo/routes/__pycache__/auth.cpython-314.pyc b/demo/routes/__pycache__/auth.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b48af22cd598c27ea1f4dae7a260c1bcf91b870a GIT binary patch literal 7265 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 literal 0 HcmV?d00001 diff --git a/demo/routes/__pycache__/customizer.cpython-314.pyc b/demo/routes/__pycache__/customizer.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfa1636f661d13c3482517ac8531d286ab768cb8 GIT binary patch literal 10217 zcmd^FYitx(maeLi4nRX<__lO1V)Y=Ljxsyg@9t$WY;&Ueo3t!|f{fLDL0X{)8HE`%DW*YIVF%5MdC;O*n(;X^WL2!>%qbi>+Y}o)+Z8*UO+$`B zr{WxRDJ~+<4!H+Cif7QPcn5uokK8j4RSi}v)y>o?IzaK3Q#HHb?RY4)r)opnw0ddD zTX)ggHP6*iy9$q#x>clYhP1szdhIIG4nx{mB3-|Vw2ODQ)4ZpvFk;?Y(xzdRHa@

3(YmKk3FOgo)Gc8oG;Ws$bL5phZOJsz! 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 literal 0 HcmV?d00001 diff --git a/demo/routes/__pycache__/dashboard.cpython-314.pyc b/demo/routes/__pycache__/dashboard.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80fc80d550a2da55f192baae3ca9cbaf7ea919c6 GIT binary patch literal 4751 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 literal 0 HcmV?d00001 diff --git a/demo/routes/__pycache__/public.cpython-314.pyc b/demo/routes/__pycache__/public.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bfd8b773e446e042dcd808af66a6a5b73debe5a GIT binary patch literal 2078 zcmZ`(O>7%Q6rR~#+w1HmuA7!tY13>PsljR#Q(}`Uq>a)fU{`4yoHiB0?c(fC?4|2n zGrR7O;gCv5Rq3HUfYd_|Jq9Esgpfc;NQDZi7bGJWDcTkl!~wx6B|RXx@MhOeOi`b- zGxO$s``(*3-@N@@ogE0|)Z>eWCLr`HQ(6MLFYAB6w2aQ89L}N)c5#MtxeV{}eQb}* z3a;SKAvfgDVK)pjpN(WhSIk7+sE-q}9T~}$`p_BNk8+_ia*7MwyPan`=X!&6`WRJ5Z`=a%0axiyP=@8+yw#&>b6SsSO>^aRVqJ|IT71crSXytda^fEZ2)sV(Emc zE-6>cf=j$^2R76tYL`{rphRZuZ3F^t?Kq%_Xq6v+1CC zuAr5ONj5!&E7e8Q&^(;fnrHMU9!B_t^_{R-M)_tf5Zpp5cgU5i$+HDZH>{#kDHKU9 zZU&l4Nx>X0xn(nfDZ9vz&^mML^-hiDFW6Mi>!fVwsaFHs z78sV%lQ5EZ)prdyq-0%4(oWc{$w|j>NpjpVtHs0GMt)$5z~|?_0=tkqsDaEi@4Th6 zxgX_Og1)p)R>2E+j(K@XS}dq=3*21vDI=&kynse|iApXG;oHsLw$;01(qIO}ed3lE@OD9;M#3t(|OHqR>Q%iPx1YIE*qzPWaM zs}``2+V?>h+VxbkdILy7`E4zaVWdNp-KZku_>+Q+UgUDZ2v_BL;q6s~31Of)8`wZE z^S#KxiAa^5Y~FAUTdCarijmS*bYd!+X%NdLwe0-N?99|j#n6Wow?xWhNYQLCSg!h( zV_QRt;}+biGo(}FAgq8w5tyr@q(r$qEx&kUtDRf6|n z)PTP>`^T`y6ypi-2VPZeZ;+K`E5u?UX#qgC0(wAQP>l}&n~=PS^R@{QN4y9CEqItT z45#wKT9rCB^&;dV(SY=xxI|p7WLv}wX{HSciwq{PgLom+F6fRIow=xy3S?p>LY}~K z_XOtD!y2d%#KXi1!EgH%5Zc8YNI^m>GgAe27$2OEVF(^k_w|YQCvJ^4q=Dt>RZ+e! zU6rnNE>Ersp)1+T*?QOO_57RlHx};;+Nva9AGtcwTN1U{607#my>R$lug^7*zedOqvsK2Grtj$Y4t!U<=hjc2 z`u;*AcDgQ}{_CN%?Fqu6^lE(n%H+yOJ(m2myB8$DnFwO%T8 zGF;m+Pw07rDg~;QjPtgmIMqc%x19t>S&=F02hd5yRy+qT9PJCrF2e<$Cu)!r9RPy@ zpA&~6sC(b7cUCeDDP0%Re)mdj^+HQ#!8u1EN#wW4APRe%l=WPXvmKy~Z7mr^pF@5E z-qxyXn9i`zf0bB8!)j7TR8>|5Rc(@6Qx_{<$nUUzH}%=zr$%KKgEgKYd2zgK>s6B+ zru$)!oyvI^2Dl82AEC(~(BuQu^#Dl^Q1lTR{0WWzB5%J@x-B;3#B%g!+;OGva^H>a z2HyUd', methods=['GET']) +def get_blocks(site_id): + """Retorna los bloques de un sitio""" + conn = sqlite3.connect(MAIN_DB) + c = conn.cursor() + c.execute('SELECT content_json FROM sites WHERE id = ?', (site_id,)) + result = c.fetchone() + conn.close() + + if not result or not result[0]: + return jsonify([]) + + try: + content = json.loads(result[0]) + return jsonify(content.get('blocks', [])) + except: + return jsonify([]) + + +@customizer_bp.route('/api/customizer/get-content/', methods=['GET']) +def get_content(site_id): + """Retorna el contenido completo (blocks + settings) de un sitio""" + conn = sqlite3.connect(MAIN_DB) + c = conn.cursor() + c.execute('SELECT content_json FROM sites WHERE id = ?', (site_id,)) + result = c.fetchone() + conn.close() + + if not result or not result[0]: + return jsonify({'success': True, 'content': {}}) + + try: + content = json.loads(result[0]) + return jsonify({'success': True, 'content': content}) + except Exception: + return jsonify({'success': True, 'content': {}}) + @customizer_bp.route('/api/customizer/add-block', methods=['POST']) def add_block(): data = request.get_json() @@ -102,6 +154,10 @@ def add_block(): c.execute('SELECT content_json FROM sites WHERE id = ?', (site_id,)) result = c.fetchone() + if not result: + conn.close() + return jsonify({'success': False, 'error': 'Sitio no encontrado'}), 404 + content = json.loads(result[0]) if result[0] else {} if 'blocks' not in content: content['blocks'] = [] diff --git a/demo/routes/get_blocks_fix.py b/demo/routes/get_blocks_fix.py new file mode 100644 index 0000000..4c180e6 --- /dev/null +++ b/demo/routes/get_blocks_fix.py @@ -0,0 +1,25 @@ +from flask import Blueprint, request, jsonify +import json + +customizer_bp = Blueprint('customizer_api', __name__) + +@customizer_bp.route('/api/customizer/get-blocks/', methods=['GET']) +def get_blocks(site_id): + """Retorna los bloques de un sitio""" + import sqlite3 + from config import MAIN_DB + + conn = sqlite3.connect(MAIN_DB) + c = conn.cursor() + c.execute('SELECT content_json FROM sites WHERE id = ?', (site_id,)) + result = c.fetchone() + conn.close() + + if not result or not result[0]: + return jsonify([]) + + try: + content = json.loads(result[0]) + return jsonify(content.get('blocks', [])) + except: + return jsonify([]) diff --git a/demo/static/test.html b/demo/static/test.html new file mode 100644 index 0000000..2e6a7c1 --- /dev/null +++ b/demo/static/test.html @@ -0,0 +1,13 @@ + + + + + Test Customizer + + + +

Abre la Consola (F12) y verás los errores del Customizer

+ + + + \ No newline at end of file diff --git a/demo/templates/admin.html b/demo/templates/admin.html index 7c0992d..029d3fd 100644 --- a/demo/templates/admin.html +++ b/demo/templates/admin.html @@ -1,36 +1,48 @@ + Admin - Demo +

🔧 Panel Admin

- +

Solicitudes Pendientes

@@ -67,10 +80,12 @@ {% endfor %} {% if not requests %} - + + + {% endif %}
No hay solicitudes pendientes
No hay solicitudes pendientes
- +

👥 Usuarios Registrados

@@ -104,10 +119,12 @@ {% endfor %} {% if not users %} - + + + {% endif %}
No hay usuarios registrados
No hay usuarios registrados
- +

🌐 Todos los Sitios

@@ -131,48 +148,50 @@ {% endfor %}
- + - + - + + \ No newline at end of file diff --git a/demo/templates/customizer.html b/demo/templates/customizer.html index 5a5a7da..4531dc1 100644 --- a/demo/templates/customizer.html +++ b/demo/templates/customizer.html @@ -1,4 +1,10 @@ + @@ -8,25 +14,19 @@ + + + + +
+ + +
+ + +
+
+
+
+
Arrastra o añade bloques para empezar
+
+
+
+
+
+
+ + + + + + + diff --git a/demo/templates/customizer.html.bak b/demo/templates/customizer.html.bak new file mode 100644 index 0000000..dcea77e --- /dev/null +++ b/demo/templates/customizer.html.bak @@ -0,0 +1,1704 @@ + + + + + + + + Personalizar - {{ site_name or 'Sitio' }} + + + + + + + +
+ +
+
+ +
{{ user_plan|upper }}
+ +
+ +
+ + + + +
+
+ Identidad del + Sitio +
+
+
+ Nombre del Sitio + +
+
+ Título Hero + +
+
+ Descripción Corta + +
+
+ Logo URL + +
+
+
+ + +
+
+ Paleta de + Colores +
+
+
+
+
+ Primario + Botones y + acentos +
+
+ + +
+
+
+
+ Secundario + Fondos suaves +
+
+ + +
+
+
+
+ Texto + Cuerpo y + títulos +
+
+ + +
+
+
+
+
+ + + +
+
+ Contacto & + Ubicación +
+
+
+ Dirección + +
+
+ Teléfono + +
+ +
+ WhatsApp (Solo números) + + Añadirá un botón flotante si se rellena. +
+
+ Email + +
+
+
+ + +
+
+ Redes + Sociales +
+
+
+ Instagram URL + +
+
+ Facebook URL + +
+
+ TikTok URL + +
+
+ LinkedIn URL + +
+
+ YouTube URL + +
+
+
+ + +
+
+ Horarios de + Atención +
+
+
+ Lunes - Viernes + +
+
+ Sábados + +
+
+ Domingos + +
+
+
+ + +
+
+ Especialidad + Culinaria +
+
+
+ Título + +
+
+ Descripción + +
+
+ URL de Imagen + +
+
+
+ + +
+
+ Capacidad +
+
+
+ Capacidad del Restaurante + + Número de personas que puede albergar el + restaurante. +
+
+
+ + +
+
+ Bloques de + Contenido +
+
+
+ +
+
+ +
+ + + +
+
+ + +
+
+ Menú + Restaurante +
+
+
+ Enlace PDF Menú + +
+
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ + +
+ + + +
+
+ +
+ + +
+ + + + + + + + + + + + diff --git a/demo/templates/customizer.html.v2.bak b/demo/templates/customizer.html.v2.bak new file mode 100644 index 0000000..dcea77e --- /dev/null +++ b/demo/templates/customizer.html.v2.bak @@ -0,0 +1,1704 @@ + + + + + + + + Personalizar - {{ site_name or 'Sitio' }} + + + + + + + +
+ +
+
+ +
{{ user_plan|upper }}
+ +
+ +
+ + + + +
+
+ Identidad del + Sitio +
+
+
+ Nombre del Sitio + +
+
+ Título Hero + +
+
+ Descripción Corta + +
+
+ Logo URL + +
+
+
+ + +
+
+ Paleta de + Colores +
+
+
+
+
+ Primario + Botones y + acentos +
+
+ + +
+
+
+
+ Secundario + Fondos suaves +
+
+ + +
+
+
+
+ Texto + Cuerpo y + títulos +
+
+ + +
+
+
+
+
+ + + +
+
+ Contacto & + Ubicación +
+
+
+ Dirección + +
+
+ Teléfono + +
+ +
+ WhatsApp (Solo números) + + Añadirá un botón flotante si se rellena. +
+
+ Email + +
+
+
+ + +
+
+ Redes + Sociales +
+
+
+ Instagram URL + +
+
+ Facebook URL + +
+
+ TikTok URL + +
+
+ LinkedIn URL + +
+
+ YouTube URL + +
+
+
+ + +
+
+ Horarios de + Atención +
+
+
+ Lunes - Viernes + +
+
+ Sábados + +
+
+ Domingos + +
+
+
+ + +
+
+ Especialidad + Culinaria +
+
+
+ Título + +
+
+ Descripción + +
+
+ URL de Imagen + +
+
+
+ + +
+
+ Capacidad +
+
+
+ Capacidad del Restaurante + + Número de personas que puede albergar el + restaurante. +
+
+
+ + +
+
+ Bloques de + Contenido +
+
+
+ +
+
+ +
+ + + +
+
+ + +
+
+ Menú + Restaurante +
+
+
+ Enlace PDF Menú + +
+
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ + +
+ + + +
+
+ +
+ + +
+ + + + + + + + + + + + diff --git a/demo/templates/customizer_pro_ready.html b/demo/templates/customizer_pro_ready.html new file mode 100644 index 0000000..dcea77e --- /dev/null +++ b/demo/templates/customizer_pro_ready.html @@ -0,0 +1,1704 @@ + + + + + + + + Personalizar - {{ site_name or 'Sitio' }} + + + + + + + +
+ +
+
+ +
{{ user_plan|upper }}
+ +
+ +
+ + + + +
+
+ Identidad del + Sitio +
+
+
+ Nombre del Sitio + +
+
+ Título Hero + +
+
+ Descripción Corta + +
+
+ Logo URL + +
+
+
+ + +
+
+ Paleta de + Colores +
+
+
+
+
+ Primario + Botones y + acentos +
+
+ + +
+
+
+
+ Secundario + Fondos suaves +
+
+ + +
+
+
+
+ Texto + Cuerpo y + títulos +
+
+ + +
+
+
+
+
+ + + +
+
+ Contacto & + Ubicación +
+
+
+ Dirección + +
+
+ Teléfono + +
+ +
+ WhatsApp (Solo números) + + Añadirá un botón flotante si se rellena. +
+
+ Email + +
+
+
+ + +
+
+ Redes + Sociales +
+
+
+ Instagram URL + +
+
+ Facebook URL + +
+
+ TikTok URL + +
+
+ LinkedIn URL + +
+
+ YouTube URL + +
+
+
+ + +
+
+ Horarios de + Atención +
+
+
+ Lunes - Viernes + +
+
+ Sábados + +
+
+ Domingos + +
+
+
+ + +
+
+ Especialidad + Culinaria +
+
+
+ Título + +
+
+ Descripción + +
+
+ URL de Imagen + +
+
+
+ + +
+
+ Capacidad +
+
+
+ Capacidad del Restaurante + + Número de personas que puede albergar el + restaurante. +
+
+
+ + +
+
+ Bloques de + Contenido +
+
+
+ +
+
+ +
+ + + +
+
+ + +
+
+ Menú + Restaurante +
+
+
+ Enlace PDF Menú + +
+
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ + +
+ + + +
+
+ +
+ + +
+ + + + + + + + + + + + diff --git a/demo/templates/demo_pro_v3.html b/demo/templates/demo_pro_v3.html new file mode 100644 index 0000000..9ee7776 --- /dev/null +++ b/demo/templates/demo_pro_v3.html @@ -0,0 +1,597 @@ + + + + + + + GKACHELE Builder - Premium V3 + + + + + + + + + + + + + + + + + +
+
+ + Identidad +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + + +
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/demo/themes/restaurante-moderno/template.html b/demo/themes/restaurante-moderno/template.html index 27f0809..e44e3c2 100644 --- a/demo/themes/restaurante-moderno/template.html +++ b/demo/themes/restaurante-moderno/template.html @@ -1,825 +1,354 @@ + {{ site_name or 'Restaurante' }} - + + + + +
- +
-

{{ hero_title - or 'Bienvenido a Nuestro Restaurante' }}

-

{{ - hero_description or 'Sabores auténticos que despiertan tus sentidos' }}

+

{{ hero_title or 'Sabores que Enamoran' + }}

+

{{ hero_description or 'Una + experiencia gastronómica de alta cocina en el corazón de la ciudad.' }}

Reservar Mesa
- -