<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Jorge Tech]]></title><description><![CDATA[Bienvenido a mi Blog donde compartiremos ideas, tutoriales, Proof Of Concepts y datos acerca del Desarrollo Web y sobre mi vida como desarrollador.]]></description><link>https://jorgetech.dev/</link><image><url>https://jorgetech.dev/favicon.png</url><title>Jorge Tech</title><link>https://jorgetech.dev/</link></image><generator>Ghost 5.80</generator><lastBuildDate>Fri, 05 Jun 2026 22:55:13 GMT</lastBuildDate><atom:link href="https://jorgetech.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Mis pensamientos y otras cosas que tengo por decir (la mayoría, de IA)]]></title><description><![CDATA[<p>No, no estaba muerto, tampoco de parranda, solo no sab&#xED;a que hacer por ac&#xE1;. Ven&#xED;a pensando en que quiero poner m&#xE1;s contenido ac&#xE1;, m&#xE1;s cosas para aprender y ense&#xF1;ar, pero &#xFA;ltimamente me queda cada vez m&#xE1;</p>]]></description><link>https://jorgetech.dev/mis-ideas-y-cosas-que-tengo-por-decir/</link><guid isPermaLink="false">6a1e1bb4faf53d43fc5978b5</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Tue, 02 Jun 2026 01:47:23 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2026/06/Agata_2026-04-01--1-.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2026/06/Agata_2026-04-01--1-.png" alt="Mis pensamientos y otras cosas que tengo por decir (la mayor&#xED;a, de IA)"><p>No, no estaba muerto, tampoco de parranda, solo no sab&#xED;a que hacer por ac&#xE1;. Ven&#xED;a pensando en que quiero poner m&#xE1;s contenido ac&#xE1;, m&#xE1;s cosas para aprender y ense&#xF1;ar, pero &#xFA;ltimamente me queda cada vez m&#xE1;s dif&#xED;cil o simplemente me complico (distraigo) m&#xE1;s en ejecutar una idea para postear.</p><p>Pero en definitiva lo que si quiero es hablar de lo que pienso, contarles las cosas que llevo en mi cabeza y ojal&#xE1; aprendamos un poquito de tecnolog&#xED;a, as&#xED; que les voy a contar cu&#xE1;les son las &#xFA;ltimas noticias y mis opiniones al respecto.</p><ul><li>Nvidia (la empresa de tarjetas de video), junto con Intel, sacaron los procesadores <a href="https://www.nvidia.com/en-us/products/rtx-spark/?ref=jorgetech.dev" rel="noreferrer">NVIDIA RTX Spark&#x2122; Superchip</a>: estos &quot;procesadores&quot; son una combinaci&#xF3;n de CPU y GPU en arquitectura ARM. No es algo estrictamente nuevo, pero a seg&#xFA;n, tienen rendimiento de una RTX 5070 en gr&#xE1;ficos y procesadores de hasta 20 n&#xFA;cleos de alta &quot;eficiencia&quot;. Esperar a que salgan a la venta a ver, porque por ahora no han anunciado precios o c&#xF3;mo se adquieren y no me puedo dar una opini&#xF3;n al respecto.</li><li>Hace tiempo (semanas atr&#xE1;s) sali&#xF3; una noticia acerca de como <a href="https://www.theguardian.com/technology/2026/apr/29/claude-ai-deletes-firm-database?ref=jorgetech.dev" rel="noreferrer">un agente de IA borr&#xF3; la base de datos y los respaldos de toda una empresa en 9 segundos</a>: en internet, YouTube y Reddit puedes encontrar varias noticias al respecto, pero en general, s&#xF3;lo quer&#xED;a decir que me caus&#xF3; algo de gracia, pero no me refiero al hecho de la desgracia ajena o burlarme de esta empresa, sino de c&#xF3;mo se &quot;impulsa&quot; el uso de agentes sin control o noci&#xF3;n porque los jefes quieren ver m&#xE1;s &quot;producci&#xF3;n&quot; m&#xE1;s r&#xE1;pido. Y si tan s&#xF3;lo uno se sentara a planificar un rato, estas situaciones se pueden prevenir f&#xE1;cilmente (o a lo mejor no hab&#xED;a forma de prevenirlo porque el usuario ni sab&#xED;a del poder o lo que este agente estaba haciendo).</li><li>Google hab&#xED;a anunciado <a href="https://www.reddit.com/r/pcmasterrace/comments/1s52joz/googles_new_ai_algorithm_might_lower_ram_prices/?ref=jorgetech.dev" rel="noreferrer">TurboQuant AI</a>, un algoritmo que reduce 6 veces el uso de memoria para los modelos de IA. Esta tecnolog&#xED;a se esperaba, o a&#xFA;n se espera, que reduzca los precios de las memorias RAM (y SSDs) debido a que muchas compa&#xF1;&#xED;as estaban desistiendo de adquirirlas para los data centers. Han pasado 2 meses y a&#xFA;n no ha regresado a la normalidad, pero yo sigo cruzando mis dedos y esperando que todo sea barato de nuevo &#x1F604;!</li></ul><p>En general, todas las noticias rondan la IA, incluyendo la de NVIDIA, ya que estos &quot;s&#xFA;per chips&quot; se ofrecen como soluciones de c&#xF3;mputo local para poner en ejecuci&#xF3;n cualquiera de estos modelos mega gigantes de IA... Y m&#xE1;s IA... Y un poco m&#xE1;s de inteligencia, pero que sea de la artificial... Y as&#xED; est&#xE1;n todas las noticias.</p><p>Entonces, tratar&#xE9; de ponerme m&#xE1;s al d&#xED;a, sacar mensajes o ense&#xF1;anzas para todos ac&#xE1;, conclusiones, cosas de mi trabajo o del simple d&#xED;a a d&#xED;a.</p><p>Pero si quieres escuchar o leer algo en especial, &#xA1;s&#xF3;lo dime y con mucho gusto te ayudo!</p>]]></content:encoded></item><item><title><![CDATA[React.js no es React Native (¿o al revés?)]]></title><description><![CDATA[<p>Si queremos saber o buscamos en Google cu&#xE1;l es la diferencia entre React Native y React.js, encontraremos un mont&#xF3;n de p&#xE1;ginas y posts que establecen la diferencia a nivel t&#xE9;cnico, o dicho de otra manera, las detalles, los protocolos y otras</p>]]></description><link>https://jorgetech.dev/react-js-no-es-react-native/</link><guid isPermaLink="false">689b44119fea8903fd07ebae</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Tue, 12 Aug 2025 14:21:47 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2025/08/feature-1000-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2025/08/feature-1000-1.png" alt="React.js no es React Native (&#xBF;o al rev&#xE9;s?)"><p>Si queremos saber o buscamos en Google cu&#xE1;l es la diferencia entre React Native y React.js, encontraremos un mont&#xF3;n de p&#xE1;ginas y posts que establecen la diferencia a nivel t&#xE9;cnico, o dicho de otra manera, las detalles, los protocolos y otras micro diferencias, sin hablar a grandes rasgos.</p><p>Para un lector no t&#xE9;cnico, estos posts no indican absolutamente nada, s&#xF3;lo mucho texto acerca de cosas HTML, CSS, renderizado, componentes, integraciones, natividad y otras &quot;buzz words&quot; que no dicen nada.</p><p>Entonces quiero colocar y dejar ac&#xE1; una de las principales diferencias que no necesariamente conseguimos en todos los posts, para espec&#xED;ficamente dejarle saber a todos esos &quot;tech head hunters&quot; que se me han acercado con alguna oferta en React en mente:</p><blockquote><strong><mark>React.js es una tecnolog&#xED;a para construir aplicativos web (frontend para p&#xE1;ginas web) y React Native es una tecnolog&#xED;a para construir aplicativos para dispositivos m&#xF3;viles agn&#xF3;stio al sistema operativo.</mark></strong></blockquote><p>Entonces, se&#xF1;or reclutador, la pr&#xF3;xima vez que te acerques diciendo &quot;tengo una oferta incre&#xED;ble para un desarrollador frontend como t&#xFA; que sepa React para crear p&#xE1;ginas web&quot; no me env&#xED;as una &#xA1;m@ldit@ &quot;prueba&quot;! para &quot;desarrollar&quot; o contestar preguntas acerca de React Native.</p><p>&#xA1;No!, por favor no lo hagas, no lo intentes, no lo fuerces con palabras tipo &quot;pero es casi lo mismo, &#xBF;no?, entonces podr&#xED;as intentarlo, &#xBF;no?&quot; o llenes un p#t0 formulario indicando &quot;el desarrollador minti&#xF3; acerca de su experiencia&quot; porque ambos sabemos que no es as&#xED;, es un error tuyo como &quot;reclutador tech&quot; al no saber las diferencias. </p>]]></content:encoded></item><item><title><![CDATA[Cómo versionar tu base de datos con "migraciones" en node.js y python]]></title><description><![CDATA[<p>TLDR; As&#xED; como tu aplicativo o soluci&#xF3;n tiene versiones, la base de datos tambi&#xE9;n debe tener versiones, las cuales pueden actualizarse a una versi&#xF3;n mayor a trav&#xE9;s de &quot;upgrades&quot; o ser revertidas a versiones anteriores con &quot;downgrades&quot;</p>]]></description><link>https://jorgetech.dev/versiones-o-migraciones-de-una-base-de-datos/</link><guid isPermaLink="false">66549f856bfb5c1355dcaece</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Wed, 11 Sep 2024 00:25:49 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/09/feature.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/09/feature.png" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python"><p>TLDR; As&#xED; como tu aplicativo o soluci&#xF3;n tiene versiones, la base de datos tambi&#xE9;n debe tener versiones, las cuales pueden actualizarse a una versi&#xF3;n mayor a trav&#xE9;s de &quot;upgrades&quot; o ser revertidas a versiones anteriores con &quot;downgrades&quot;. Esto asegura que tu aplicativo pueda ser usado en diferentes etapas de su evoluci&#xF3;n de acuerdo a los requerimientos con integraciones o actualizaciones del cliente. En este post hablamos sobre qu&#xE9; son &quot;migraciones&quot; de una base de datos y construimos 3 ejemplos para aplicativos en Node.js con Sequelize, Node.js con TypeORM y Flask con SQLAlchemy y Alembic.</p><p>Enlaces a documentaciones oficiales:</p><ul><li><a href="https://sequelize.org/master/manual/migrations.html?ref=jorgetech.dev">Documentaci&#xF3;n de Sequelize</a></li><li><a href="https://typeorm.io/?ref=jorgetech.dev#/">Documentaci&#xF3;n de TypeORM</a></li><li><a href="https://alembic.sqlalchemy.org/en/latest/?ref=jorgetech.dev">Documentaci&#xF3;n de Alembic</a></li><li><a href="https://flask-migrate.readthedocs.io/en/latest/?ref=jorgetech.dev">Documentaci&#xF3;n de Flask-Migrate</a></li></ul><p>Tambi&#xE9;n te dejo este enlace donde se encuentran los ejemplos de es Post en GitHub: <a href="https://github.com/jcontrerasprince/migraciones-nodejs-python?ref=jorgetech.dev">Migraciones con Node.js y Python</a>.</p><h2 id="entonces-%C2%BFqu%C3%A9-son-las-migraciones-en-una-base-de-datos">Entonces, &#xBF;Qu&#xE9; son las &quot;migraciones&quot; en una base de datos?</h2><p>De manera muy corta y sencilla, las migraciones en una base de datos es el versionamiento del esquema, o de las tablas o de la relaci&#xF3;n entre entidades (ERD -Entity Relationship Diagram-), de acuerdo con la evoluci&#xF3;n de tu aplicativo.</p><h2 id="ejemplo-de-un-aplicativo-web-de-correspondencia">Ejemplo de un aplicativo web de correspondencia</h2><p>En un ejemplo pr&#xE1;ctico: imagina que has desarrollado un aplicativo web para el seguimiento de env&#xED;o de correspondencia f&#xED;sica, como sobres y paquetes, que consta de un <em>frontend</em>, un <em>backend </em>y una base de datos.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=1785&amp;x=-166&amp;y=-220&amp;w=1432&amp;h=851&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%209631914b6c69bcb6d88cfb47d7502e27a60e1cbc62e19e93d0c1364d507bed77-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="1432" height="851"><figcaption><span style="white-space: pre-wrap;">Diagrama que muestra el flujo de interacciones entre distintas capas de un sistema de software. En este caso el usuario interact&#xFA;a con la capa de cliente y la informaci&#xF3;n viaja hasta la base de datos.</span></figcaption></figure><p>Este aplicativo tiene varias funciones y caracter&#xED;sticas, sin embargo, vamos a enfocarnos que un usuario final tiene la posibilidad de ver el pr&#xF3;ximo env&#xED;o a recibir en una interfaz que le muestra desde donde viene, a donde se va a entregar y qu&#xE9; d&#xED;a ser&#xE1; entregando, incluyendo una aproximaci&#xF3;n en la parte del d&#xED;a (ma&#xF1;ana, tarde o noche).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=1790&amp;x=1249&amp;y=-360&amp;w=2426&amp;h=1100&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%20466d9f755172fd4e4a15a2e1603198a7069f50721a1ce0af1227a9e969b75eb5-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="2426" height="1100"><figcaption><span style="white-space: pre-wrap;">Visualizaci&#xF3;n de informaci&#xF3;n de aplicativo ejemplo junto con los campos de informaci&#xF3;n disponibles en la versi&#xF3;n 1.0.</span></figcaption></figure><p>El aplicativo es perfecto, cumple con todas las solicitudes de los <em>stake-holders</em> o interesados y tus jefes est&#xE1;n sumamente contentos con el trabajo realizado.</p><p>Los usuarios finales deciden invertir para a&#xF1;adirle una segunda serie de funcionalidades y caracter&#xED;sticas a tu aplicativo, entre las cuales, est&#xE1; la posibilidad de indicar el peso total del paquete pr&#xF3;ximo a recibir porque los clientes quieren saber si es algo muy grande que deben manejar en la entrega.</p><p>Bien, el cambio no es dif&#xED;cil, es solo a&#xF1;adir una columna nueva en la base de datos, actualizar el esquema o clase en la l&#xF3;gica del <em>backend </em>y habilitar un campo en la interfaz del <em>frontend </em>para introducir esta informaci&#xF3;n.</p><p>Tu plan para llevar a cabo esta actualizaci&#xF3;n, y la de las dem&#xE1;s funcionalidades y caracter&#xED;sticas, es actualizar los repositorios, por lo cual ya tus aplicativos <em>frontend </em>y <em>backend </em>tienen la nueva l&#xF3;gica, y para tu base de datos, decides que entrar&#xE1;s al servidor de producci&#xF3;n y har&#xE1;s este cambio manualmente con un <code>ALTER TABLE pedidos ADD peso_kg DECIMAL (16, 4)</code>.</p><p>Como podr&#xE1;s notar, esto de entrar a los servidores en producci&#xF3;n es un &#xA1;no no no! Existen muchos riesgos y peligros, cr&#xE9;eme. Por m&#xE1;s experimentado que uno sea, siempre va a ocurrir el error al momento de alterar o modificar algo en una base de datos en producci&#xF3;n (por desgracia, aprend&#xED; esto a las malas).</p><h2 id="la-migraci%C3%B3n-de-la-base-de-datos-en-tu-aplicativo-de-correspondencia">La &quot;migraci&#xF3;n&quot; de la base de datos en tu aplicativo de correspondencia</h2><p>Para resolver este inconveniente de actualizaci&#xF3;n del esquema de la base de datos, creamos o &quot;generamos&quot; migraciones que se encarguen de alterar, a&#xF1;adir o eliminar objetos de tu base de datos de acuerdo al requerimiento de las nuevas versiones.</p><p>Pero recuerda esto super importante: as&#xED; como en tu control de versiones en GitHub, GitLab, BitBucket, Azure Devops, o el servicio que est&#xE9;s usando, debe existir tanto una forma de subir o mejorar tu versi&#xF3;n de base de datos (&quot;upgrade&quot;), como una forma de bajar o degradar la versi&#xF3;n (&quot;downgrade&quot;).</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1.png" width="1763" height="827" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1.png 1600w, https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1.png 1763w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1--1-.png" width="2000" height="886" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1--1-.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1--1-.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1--1-.png 1600w, https://jorgetech.dev/content/images/size/w2400/2024/06/Migraciones---Page-1--1-.png 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1--2-.png" width="2000" height="855" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1--2-.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1--2-.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1--2-.png 1600w, https://jorgetech.dev/content/images/size/w2400/2024/06/Migraciones---Page-1--2-.png 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Diagrama que muestra la evoluci&#xF3;n de la base de datos de acuerdo a los requerimientos. Tambi&#xE9;n se observa una visualizaci&#xF3;n de la aplicaci&#xF3;n de ejemplo en su versi&#xF3;n 2.0.</span></p></figcaption></figure><p>Entonces vamos a crear 2 m&#xE9;todos o funciones que hagan exactamente esto mismo:</p><ol><li>La funci&#xF3;n de <code>upgrade</code> se encarga mejorar la base de datos desde la versi&#xF3;n v1.0 a la v2.0.</li><li>La funci&#xF3;n de <code>downgrade</code> se encarga de degradar la base de datos desde la versi&#xF3;n v2.0 a la v.10.</li></ol><p>Puede suceder el caso de que un error inesperado afecta producci&#xF3;n y la mejor soluci&#xF3;n es revertir todos los cambios y retornar a una versi&#xF3;n anterior. En nuestro ejemplo ser&#xED;a regresar a la versi&#xF3;n v1.0, por lo tanto es importante tener la funci&#xF3;n <code>downgrade</code> disponible.</p><p>As&#xED; como hacemos <code>git revert</code> para revertir cambios en el repositorio y retornarlo a un estado anterior, lo mismo logramos con el m&#xE9;todo <code>downgrade</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=2477&amp;x=6677&amp;y=-197&amp;w=926&amp;h=794&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%2085efee722b60ee7537204a73b9fb3cfea9137d32c9fc969687a42ec68dafdc12-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="926" height="794"><figcaption><span style="white-space: pre-wrap;">Representaci&#xF3;n de c&#xF3;mo se hace un cambio de versi&#xF3;n usando el c&#xF3;digo fuente y un aplicativo para el manejo de versionamiento y el parecido con las migraciones de bases de datos.</span></figcaption></figure><p>Listo, de esta forma logras varios objetivos en tu aplicativo:</p><ul><li>Manejar tu base de datos con un control de versiones.</li><li>Hacer actualizaciones de forma programada con cada salida o liberaci&#xF3;n de nueva versi&#xF3;n de tu aplicativo.</li><li>Tener un camino o v&#xED;a para retornar los cambios en cualquier punto de tu aplicativo.</li></ul><p>Si, a pesar de que es mayor trabajo al momento de desarrollar tu aplicativo, es la mejor forma de tener un control y trazabilidad completa de tu esquema de base de datos.</p><h2 id="ahora-%C2%BFhay-algunos-ejemplos-pr%C3%A1cticos-que-podamos-usar-en-nodejs-o-python">Ahora, &#xBF;Hay algunos ejemplos pr&#xE1;cticos que podamos usar en Node.js o Python?</h2><p>Si, para esta pregunta te voy a presentar 2 ejemplos en Node.js, uno usando la librer&#xED;a Sequelize y otro usando la librer&#xED;a TypeORM. El &#xFA;ltimo ejemplo lo haremos en Python con la librer&#xED;a Alembic.</p><p>De nuevo, todo el c&#xF3;digo referente a estos ejemplos lo encuentras en <a href="https://github.com/jcontrerasprince/migraciones-nodejs-python?ref=jorgetech.dev">Migraciones con Node.js y Python</a></p><h3 id="aplicativo-en-nodejs-javascript-usando-sequelize">Aplicativo en Node.js (JavaScript) usando Sequelize</h3><p>En este primer ejemplo creamos una aplicaci&#xF3;n desde cero en Node.js usando las librerias dotenv, express, pg, pg-hstore y sequelize. Como dependencias de desarrollador usamos las librerias de nodemon y sequelize-cli.</p><p>Como vamos a usar dotenv, creamos un archivo <code>.env</code> el cual tendr&#xE1; la variable <code>DATABASE_CONNECTION_URI = postgres://usuario:contrase&#xF1;a@localhost:5432/migrations_sequelize</code> apuntando a la base de datos.</p><p>Creamos el archivo <code>.sequelizerc</code> para indicar los paths hacia los distintos archivos de configuraci&#xF3;n. En este ejemplo indicamos donde se encuentra el archivo <code>config.js</code>.</p><p>En el archivo <code>config.js</code> indicamos el objeto &quot;development&quot; para nuestras variables en el ambiente de desarrollo e indicamos los valores de <code>url</code> y <code>dialect</code> para nuestra base de datos.</p><p>Creamos el directorio <code>migrations</code> e indicamos nuestras migraciones creando documentos con el formato <code>&lt;timestamp&gt;-&lt;nombre-migracion&gt;.js</code>. Y en este archivo definimos las funciones <code>up</code> y <code>down</code> a ser exportadas donde est&#xE1; la l&#xF3;gica de subida de versi&#xF3;n y bajada de versi&#xF3;n de la base de datos.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">&quot;use strict&quot;;

/** @type {import(&apos;sequelize-cli&apos;).Migration} */
module.exports = {
  up: (queryInterface, Sequelize) =&gt; {
    // l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n
  },
  down: (queryInterface, Sequelize) =&gt; {
    // l&#xF3;gica para revertir los cambios
  },
};</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>up</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>down</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en Sequelize.</span></p></figcaption></figure><p>Otra forma de crear este archivo es a trav&#xE9;s del uso de <code>sequelize-cli</code>, cuya herramienta nos permite usar el comando <code>npx sequelize-cli model:generate --name MyModel</code> el cual crear&#xE1; un archivo con el nombre <code>&lt;timestamp&gt;-create-user.js</code> en la carpeta <code>migrations</code>.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://sequelize.org/master/manual/migrations.html?ref=jorgetech.dev" rel="noreferrer"><em>Sequelize Migrations</em></a><em>.</em></p><h3 id="aplicativo-en-nodejs-typescript-usando-typeorm">Aplicativo en Node.js (TypeScript) usando TypeORM</h3><p>En este segundo ejemplo creamos una aplicaci&#xF3;n desde cero en Node.js usando las librerias dotenv, express, pg, reflect-metadata y typeorm. Como dependencias de desarrollador usamos las librerias de @types/express, @types/node, nodemon, ts-node y typescript.</p><p>Con el uso de dotenv vamos a crear un archivo <code>.env</code> con la variable <code>DATABASE_CONNECTION_URI = postgres://usuario:contrase&#xF1;a@localhost:5432/migrations_typeorm</code> apuntando a la base de datos.</p><p>Creamos un archivo llamado <code>data-source.ts</code> donde usaremos la clase <code>DataSource</code> de typeorm. Adicionalmente importaremos todas las clases que representen un modelo de datos de las entidades del proyecto. Finalizamos el archivo exportando nuestro objeto <code>AppDataSource</code> con la configuraci&#xF3;n de nuestra fuente de datos.</p><p>Al ser un proyecto en TypeScript, tambi&#xE9;n se debe configurar el archivo <code>tsconfig.json</code>, el cu&#xE1;l depender&#xE1; de cada proyecto o de tus preferencias en este framework.</p><p>Y por &#xFA;ltimo creamos nuestros archivos de migraciones, los cuales llevan un formato parecido al de Sequelize: <code>&lt;timestamp&gt;-&lt;NombreMigracion&gt;.ts</code> (n&#xF3;tese el uso del formato <em>Pascal Case</em>).</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript">import { MigrationInterface, QueryRunner } from &quot;typeorm&quot;

export class PostRefactoringTIMESTAMP implements MigrationInterface {
    async up(queryRunner: QueryRunner): Promise&lt;void&gt; {
      // // l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n
    }

    async down(queryRunner: QueryRunner): Promise&lt;void&gt; {
      // l&#xF3;gica para revertir los cambios
    }
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>up</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>down</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en TypeORM.</span></p></figcaption></figure><p>Otra forma de crear este archivo es a trav&#xE9;s del cli usando el comando <code>typeorm migration:create ./path-to-migrations-dir/PostRefactoring</code>, el cual crear&#xE1; el archivo con el nombre de migraci&#xF3;n <code>PostRefactoring</code> usando el timestamp actual, para que puedas modificarlo a tu gusto.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://typeorm.io/migrations?ref=jorgetech.dev" rel="noreferrer"><em>TypeORM Migrations</em></a><em>.</em></p><h3 id="aplicativo-en-flask-python-usando-sqlalchemy-y-alembic">Aplicativo en Flask (Python) usando SQLAlchemy y Alembic</h3><p>Y para nuestro &#xFA;ltimo ejemplo creamos una aplicaci&#xF3;n desde cero en Flask usando las librer&#xED;as Flask, SQLAlchemy, Flask-SQLAlchemy, Alembic, Flask-Migrate y psycopg2-binary.</p><p>En este aplicativo tambi&#xE9;n podemos usar librer&#xED;as para el uso de variables de entorno como lo es python-dotenv, sin embargo, en nuestro ejemplo nos saltamos esta parte. De todas maneras recuerda mantener la seguridad de tu aplicativo usando variables de entorno y secretos para las credenciales de los aplicativos. Tambi&#xE9;n para el caso de Flask vamos a crear un archivo <code>config.py</code> donde exportamos la clase Config con las variables <code>SQLALCHEMY_DATABASE_URI = &apos;postgresql://postgres:password@localhost:5432/migrations_alembic&apos;</code> y <code>SQLALCHEMY_TRACK_MODIFICATIONS = False</code>.</p><p>El uso de Alembic para las migraciones tiene una l&#xF3;gica distinta a las de Sequelize y TypeORM: las migraciones no se crean a mano sino a trav&#xE9;s de cli (aunque &#xE9;stas &#xFA;ltimas tambi&#xE9;n tienen la tienen opci&#xF3;n, en Alembic es el m&#xE9;todo preferido). Entonces, en este caso usaremos el comando <code>flask db init</code> para inicializar nuestro sistema de migraciones, para luego usar el comando <code>flask db migrate -m &quot;Initial migration.&quot;</code> y por &#xFA;ltimo el comando <code>flask db upgrade</code>. En estos 3 pasos hemos inicializado la carpeta de migraciones (en el archivo app.py le hemos indicado al objeto <code>migrate</code> que usaremos el nombre de <code>migraciones</code> como carpeta).</p><p>En Alembic las migraciones van en la carpeta <code>versions</code> y no van enumeradas de menor a mayor a trav&#xE9;s del uso de timestamp o numeraci&#xF3;n con en las otras librerias, sino que se asigna una cadena hexadecimal de 12 car&#xE1;cteres o un &quot;12 digit hex code&quot;, y el orden de las versiones se indica a trav&#xE9;s de las variables <code>revision</code> y <code>down_revision</code> dentro de cada archivo (pudiendo complicar a simple vista el orden de las migraciones para nosotros los humanos).</p><p>Dentro de cada archivo se encuentra el c&#xF3;digo a ejecutar para subir o bajar de versi&#xF3;n.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&quot;&quot;&quot;Initial migration.

Revision ID: de3dad1e7bad
Revises: 
Create Date: 2024-06-26 12:26:49.288399

&quot;&quot;&quot;
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = &apos;de3dad1e7bad&apos;
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n


def downgrade():
    # l&#xF3;gica para revertir los cambios</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>upgrade</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>downgrade</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en Sequelize.</span></p></figcaption></figure><p>Y listo, usamos el comando <code>flask db upgrade</code> para actualizar nuestra base de datos para tener nuestro proyecto en Flask al d&#xED;a.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://flask-migrate.readthedocs.io/en/latest/?ref=jorgetech.dev" rel="noreferrer"><em>Flask-Migrate usando Alembic</em></a><em>.</em></p><h2 id="conclusiones">Conclusiones</h2><p>Las migraciones de bases de datos son esenciales para mantener la integridad y la funcionalidad de una aplicaci&#xF3;n en crecimiento. As&#xED; como el c&#xF3;digo de una aplicaci&#xF3;n evoluciona, tambi&#xE9;n lo debe hacer la estructura de su base de datos. Al implementar un enfoque estructurado para las migraciones, nos podemos asegurar que las actualizaciones y mejoras se realicen de manera controlada y reproducible, minimizando el riesgo de errores en producci&#xF3;n. Esto es especialmente crucial en entornos donde la precisi&#xF3;n y la consistencia son fundamentales, como en sistemas que manejan grandes vol&#xFA;menes de datos o que tienen requisitos regulatorios estrictos.</p><p>Incorporar tanto las funciones de &quot;upgrade&quot; como de &quot;downgrade&quot; en el proceso de migraci&#xF3;n no solo permite mejorar la base de datos, sino tambi&#xE9;n revertir cambios en caso de errores, as&#xED; como tambi&#xE9;n el uso de herramientas como Sequelize, TypeORM y Alembic hace que la creaci&#xF3;n y gesti&#xF3;n de migraciones sea m&#xE1;s accesible y menos propensa a fallas.</p><p>Y finalmente, adoptar un enfoque proactivo en la gesti&#xF3;n de migraciones refuerza el compromiso de un equipo de desarrollo con la calidad y la estabilidad del software. A medida que las aplicaciones crecen en complejidad y alcance, la capacidad de gestionar cambios en la base de datos de manera eficaz se convierte en un diferenciador clave.</p>]]></content:encoded></item><item><title><![CDATA[Configurar correos en Ghost]]></title><description><![CDATA[<p>TLDR: Cre&#xE9; mi primera p&#xE1;gina web para mi marca personal y me falt&#xF3; configurar el correo electr&#xF3;nico. Son 2 tipos de env&#xED;o de correo que usa Ghost: 1) emails transaccionales para registrarse en el portal o solicitud de correo para reinicio de</p>]]></description><link>https://jorgetech.dev/configurar-correos-en-ghost/</link><guid isPermaLink="false">6621330c6bfb5c1355dcad8d</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Fri, 19 Apr 2024 20:32:46 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/003-feature-v2.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/003-feature-v2.png" alt="Configurar correos en Ghost"><p>TLDR: Cre&#xE9; mi primera p&#xE1;gina web para mi marca personal y me falt&#xF3; configurar el correo electr&#xF3;nico. Son 2 tipos de env&#xED;o de correo que usa Ghost: 1) emails transaccionales para registrarse en el portal o solicitud de correo para reinicio de contrase&#xF1;a (a trav&#xE9;s de SMTP), y 2) emails de marketing para el env&#xED;o de newsletter (a trav&#xE9;s de una API para correos masivos -bulk-). Intent&#xE9; configurar <a href="https://www.mailgun.com/?ref=jorgetech.dev" rel="noreferrer">Mailgun</a> como se recomienda en la <a href="https://ghost.org/docs/config/?ref=jorgetech.dev#mail" rel="noreferrer">documentaci&#xF3;n</a>, pero me salt&#xE9; un detalle y como tampoco quiero hacer mucho &#xE9;nfasis en el Newsletter, eleg&#xED; AWS SES como proveedor de env&#xED;o de correos y s&#xF3;lo dej&#xE9; configurado los env&#xED;os transaccionales.</p><h2 id="%C2%BFmailgun-o-aws-ses">&#xBF;Mailgun o AWS SES?</h2><p>Al momento de levantar un portal de Ghost, una parte f&#xE1;cil de dejar de &#xFA;ltimo es la elecci&#xF3;n de proveedor de env&#xED;o de correos, entonces &#xBF;qu&#xE9; significa esto?. Recuerda que tu portal tiene la opci&#xF3;n de crear usuarios para suscriptores o recibir un Newsletter apenas un post haya sido publicado.</p><p>La documentaci&#xF3;n de Ghost favorece Mailgun y explica que es muy f&#xE1;cil crear una cuenta, por lo que inicialmente elegir esta opci&#xF3;n suena muy f&#xE1;cil, y m&#xE1;s cu&#xE1;ndo te menciona que tiene una suscripci&#xF3;n de &quot;pay-as-you-go&quot;, la cual cobra por cada correo enviado. Y adicional suma al hecho de que Mailgun no cobra cuando la factura es menor a $ 0.50 en USD, por lo que podemos decir que tenemos alrededor de 600 env&#xED;os de correos de forma gratuita.</p><p>Pero parte de esta informaci&#xF3;n, para hoy 18 de abril de 2024, est&#xE1; un poco desactualizada.</p><p><mark>Actualmente no hay forma de elegir el plan &quot;pay-as-you-go&quot;, o no aparece entre las </mark><a href="https://www.mailgun.com/pricing/?ref=jorgetech.dev" rel="noreferrer"><mark>opciones de plan</mark></a><mark>, por lo que se debe empezar desde el m&#xE1;s econ&#xF3;mico de $ 35.00 para una capacidad m&#xE1;xima de 50,000 correos, lo cual no es gratis como podemos leer.</mark></p><p>Esto hace que Mailgun no suene tan favorable. Y como por ahora el env&#xED;o de Newsletter no es primordial, podemos obviarlo y configurar alg&#xFA;n servicio para s&#xF3;lo los correos transaccionales, lo cu&#xE1;l se explica m&#xE1;s adelante.</p><p>Por lo que respondiendo a esta pregunta: si quieres enviar todo tipo de correos, transaccionales y de marketing, <strong>configura Mailgun</strong> (incluso en la &#xFA;ltima secci&#xF3;n te explico como crear una cuenta &quot;pay-as-you-go&quot; actualmente), y si solo necesitas para el manejo de usuarios, solo correos transaccionales, entonces <strong>configura AWS SES</strong>, o Gmail, o Mailchimp, o cualquier otro servicio que desees.</p><h2 id="pero-%C2%BFqu%C3%A9-son-correos-transaccionales-y-qu%C3%A9-son-correos-de-marketing">Pero, &#xBF;qu&#xE9; son correos transaccionales y qu&#xE9; son correos de marketing?</h2><p>La administraci&#xF3;n de usuarios creados con el correo electr&#xF3;nico requiere enviar informaci&#xF3;n privada para verificar la direcci&#xF3;n o enviar un email para cambiar la contrase&#xF1;a con alg&#xFA;n token. Este tipo de comunicaci&#xF3;n son correos transaccionales y es parte vital de cualquier portal.</p><p>Cuando queremos enviar emails con una vista o invitaci&#xF3;n a leer el &#xFA;ltimo post, informaci&#xF3;n privada que es s&#xF3;lo para ciertos miembros, o simplemente enviar un correo para publicidad, a esto se le llama correo de marketing.</p><p>Ghost solo permite el servicio de Mailgun para el env&#xED;o de correos de marketing, y tienen varias razones <a href="https://ghost.org/docs/faq/mailgun-newsletters/?ref=jorgetech.dev" rel="noreferrer">explicadas en su portal</a>, y el TLDR: Mailgun usa mecanismos para evitar que los servidores de correos sean clasificados como spam.</p><h2 id="entonces-%C2%BFc%C3%B3mo-configuro-aws-ses">Entonces, &#xBF;c&#xF3;mo configuro AWS SES?</h2><p>Una vez decidido es simplemente entrar a nuestra cuenta de AWS, ir a la secci&#xF3;n de SES (Simple Email Service) y registrar nuestro dominio. En el post pasado explicaba el uso de AWS Route 53 y la adquisici&#xF3;n del dominio a trav&#xE9;s de Namecheap. Como la configuraci&#xF3;n de los servidores de Namecheap apunta a los servidores de AWS, espec&#xED;ficamente hacia los de Route 53, registrar el dominio en AWS SES es muy f&#xE1;cil, sobre todo porque al ser servicios de Amazon, de una vez los datos de los servidores y dem&#xE1;s son registrados de forma autom&#xE1;tica.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-103655.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1831" height="702" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-103655.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-103655.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-103655.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-103655.png 1831w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Crear una identidad para el dominio.</span></figcaption></figure><p>Una vez registrado, es s&#xF3;lo esperar que se haya verificado el dominio y listo, ya tenemos esta parte configurada, ya podemos enviar correos de parte de jorgetech.dev. Ahora nos queda probar, pero para eso, tenemos que verificar una cuenta de correo electr&#xF3;nico.</p><figure class="kg-card kg-image-card"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104207.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1850" height="820" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-104207.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-104207.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-104207.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104207.png 1850w" sizes="(min-width: 720px) 720px"></figure><p>AWS SES tambi&#xE9;n permite verificar correos electr&#xF3;nicos, comprobar que nos pertenece y enviar mensajes con el correo verificado como destinatario, pero esta no es la raz&#xF3;n de verificar el correo. Resulta que al registrar tu dominio, este a&#xFA;n contin&#xFA;a estando en un ambiente de Sandbox, o de pruebas, y solo es posible enviar correos a direcciones autorizadas o verificadas. Es m&#xE1;s un tema de seguridad que otra cosa, y la forma de salir de este ambiente y transformarlo a uno de producci&#xF3;n, es haciendo la solicitud a AWS. Recuerda que esto sube los topes de tus mensajes.</p><p>Teniendo registrado el dominio y con correo electr&#xF3;nico (puede ser uno personal), podemos enviar correos de prueba para verificar que todo est&#xE1; funcionando como deber&#xED;a. Una vez todo parece ir bien, lo que falta es solicitar salir del ambiente Sandbox.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104727.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1840" height="872" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-104727.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-104727.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-104727.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104727.png 1840w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Registro de un correo electr&#xF3;nico como identidad.</span></figcaption></figure><h3 id="abrir-un-caso-en-aws-para-salir-de-ambiente-sandbox">Abrir un caso en AWS para salir de ambiente Sandbox</h3><p>Esta parte es la m&#xE1;s &quot;personal&quot;, y digo personal, porque es un correo que una persona debe verificar y autorizar, o eso parece.</p><p>Mientras est&#xE1;s en el Dashboard de SES puedes ver un cartel azul aparecer indicando abrir un caso solicitando la salida del ambiente Sandbox. En el caso, se debe seleccionar si el uso del correo es para transacciones o para marketing. La idea es colocar una descripci&#xF3;n de para qu&#xE9; vas a usar el correo, c&#xF3;mo piensas manejar la distribuci&#xF3;n de correos, c&#xF3;mo vas a manejar los usuarios, que proceso tienes para cuando un usuario no quiere recibir correos o el famoso bot&#xF3;n de &quot;unsubscribe&quot; y dem&#xE1;s.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105338.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1834" height="842" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-105338.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-105338.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-105338.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105338.png 1834w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Caso creado para la solicitud de salida de Sandbox.</span></figcaption></figure><p>Una vez creado este caso o ticket, te llegar&#xE1;n correos con la apertura de tu caso y probablemente te pidan m&#xE1;s detalles. Una vez transcurra un tiempo entre 24 y 48 horas, te responder&#xE1;n con los t&#xE9;rminos y condiciones y la aprobaci&#xF3;n de la salida de ambiente Sandbox.</p><p>A partir de aqu&#xED;, puedes eliminar tu correo personal como verificado y volver a probar desde SES. S&#xF3;lo queda pendiente la configuraci&#xF3;n Ghost.</p><h2 id="configuraci%C3%B3n-de-correo-en-ghost">Configuraci&#xF3;n de correo en Ghost</h2><p>Ac&#xE1; es sencillo, es ingresar a la instancia EC2 a trav&#xE9;s de SSH y buscar el archivo de configuraci&#xF3;n de ghost para cambiar algunos valores. Este documento est&#xE1; en la direcci&#xF3;n  <code>/opt/bitnami/ghost/config.production.json</code> y en el portal de Bitnami <a href="https://docs.bitnami.com/general/apps/ghost/configuration/configure-smtp/?ref=jorgetech.dev" rel="noreferrer">puedes encontrar m&#xE1;s informaci&#xF3;n de configuraci&#xF3;n</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105928.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1094" height="568" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-105928.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-105928.png 1000w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105928.png 1094w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Archivo de configuraci&#xF3;n de Ghost.</span></figcaption></figure><p>Recuerda que este es el archivo JSON de configuraci&#xF3;n de Ghost para varias opciones, como las credenciales de la base de datos o la url de la p&#xE1;gina.</p><p>Luego, puedes ir al portal administrativo de Ghost e intentar ingresar al portal administrativo, dar click en el bot&#xF3;n de &quot;Forgot password&quot; y esperar que el correo de reinicio de contrase&#xF1;a llegue tu bandeja, y listo: ya tienes configurado tu sistema de correo en Ghost.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-110732.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1852" height="903" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-110732.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-110732.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-110732.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-110732.png 1852w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Solicitar correo de recuperaci&#xF3;n de contrase&#xF1;a.</span></figcaption></figure><h2 id="%C2%BFy-c%C3%B3mo-hago-para-crear-una-cuenta-mailgun-con-el-plan-pay-as-you-go">&#xBF;Y c&#xF3;mo hago para crear una cuenta Mailgun con el plan &quot;pay-as-you-go&quot;?</h2><p>Listo, entonces queremos crear una cuenta en Mailgun, pero no queremos pagar por un plan por una capacidad m&#xE1;xima de mensajes, sino pagar por lo que usamos. Tener disponible 50,000 mensajes para alguien que est&#xE1; empezando es demasiado.</p><p>En el portal de Ghost indica que se puede obtener una cuenta &quot;pay-as-you-go&quot;, pero no indica exactamente c&#xF3;mo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-4.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1436" height="884" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-4.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-4.png 1000w, https://jorgetech.dev/content/images/2024/04/image-4.png 1436w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Secci&#xF3;n &quot;Why do I have to set up Mailgun?&quot; de Ghost.</span></figcaption></figure><p>Alguien ya indic&#xF3; en los foros de Ghost que no es posible abrir una cuenta y seleccionar la opci&#xF3;n de pagar a medida que se usa, y preguntando qu&#xE9; opciones hab&#xED;an, se le respondi&#xF3; como hacer para obtener este plan: <mark>al momento de crear una nueva cuenta, llenar el formulario del m&#xE9;todo de pago, seleccionar un mes de prueba gratis, usar el servicio por unos d&#xED;as y luego darse de baja del plan, por lo que autom&#xE1;ticamente la plataforma te asigna en el plan de &quot;pay-as-you-go&quot;</mark>. De esta forma no se cobra por ning&#xFA;n plan y puedes usar el servicio pagando por los mensajes que env&#xED;es, pero si estos son menos de aproximadamente 600, puede que no se te facture al respecto.</p><p><a href="https://forum.ghost.org/t/solved-mailgun-has-changed-pay-as-you-go-offer-how-to-send-cheap-emails-with-mailgun/32850?ref=jorgetech.dev" rel="noreferrer">En este enlace</a> tienes toda la informaci&#xF3;n del caso y las respuestas que estamos mencionando.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-5.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1386" height="915" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-5.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-5.png 1000w, https://jorgetech.dev/content/images/2024/04/image-5.png 1386w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Capturas de pantalla de c&#xF3;mo obtener el plan &quot;pay-as-you-go&quot; de Mailgun.</span></figcaption></figure><p>El error que comet&#xED; fue que cre&#xE9; la cuenta directamente y ya no me es posible seleccionar una prueba por un tiempo o <em>trial</em>, por lo que debo cancelar mi cuenta, esperar un tiempo y volverla a crear con la opci&#xF3;n de prueba, usarla unos d&#xED;as (antes del corte) y luego cancelar la suscripci&#xF3;n para que se me asigne el plan que deseo.</p><p>En resumen, el portal ya se encuentra habilitado para suscribirse. Por los momentos no tengo un plan de Newsletter o secciones exclusivas para miembros, pero si deseo a&#xF1;adir esto en un futuro, dependiendo de como vayan las cosas.</p><p>Si tienes alguna duda de c&#xF3;mo hacer alguno de estos pasos, d&#xE9;jame saber y con seguridad podemos resolverlo. !Feliz d&#xED;a!</p>]]></content:encoded></item><item><title><![CDATA[Mi propio blog en Ghost]]></title><description><![CDATA[<p>TLDR: Quise crear mi propia p&#xE1;gina web tipo Blog, usando alguna tecnolog&#xED;a que fuera novedosa, descartando viejos conocidos en PHP como WordPress o Drupal. Tambi&#xE9;n quise implementar el proceso de levantamiento usando alg&#xFA;n servicio de c&#xF3;mputo en la nube como</p>]]></description><link>https://jorgetech.dev/mi-propio-blog-en-ghost/</link><guid isPermaLink="false">66105b82aa35cb4bdf8c2e94</guid><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Fri, 12 Apr 2024 20:52:00 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/ghost-blog.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/ghost-blog.png" alt="Mi propio blog en Ghost"><p>TLDR: Quise crear mi propia p&#xE1;gina web tipo Blog, usando alguna tecnolog&#xED;a que fuera novedosa, descartando viejos conocidos en PHP como WordPress o Drupal. Tambi&#xE9;n quise implementar el proceso de levantamiento usando alg&#xFA;n servicio de c&#xF3;mputo en la nube como AWS o Vercel. Termin&#xE9; eligiendo <a href="https://ghost.org/?ref=jorgetech.dev" rel="noreferrer">Ghost</a> como tecnolog&#xED;a, descartando el servicio que ellos ofrecen para los blogs y creando una instancia en AWS EC2. No sali&#xF3; como esperaba, tuve unos inconvenientes y me apoy&#xE9; con unas im&#xE1;genes ya existentes de <a href="https://bitnami.com/stack/ghost/cloud/aws?ref=jorgetech.dev" rel="noreferrer">Bitnami</a> para la puesta en servicio. Lo dem&#xE1;s fue configuraci&#xF3;n y aqu&#xED; estamos.</p><hr><h2 id="elecci%C3%B3n-de-ghost-razones-personales-puedes-saltar-esta-secci%C3%B3n">Elecci&#xF3;n de Ghost (razones personales, puedes saltar esta secci&#xF3;n)</h2><p>Primero, dentro de lo que quer&#xED;a hacer, investigu&#xE9; varias herramientas para creaci&#xF3;n de p&#xE1;ginas web (no solo blogs) que sean contenidas en un solo aplicativo (<em>backend</em> y <em>frontend</em> en el mismo despliegue) y que est&#xE9;n basadas en node.js.</p><p>En el mundo de node.js no hay muchas opciones, las m&#xE1;s relevantes son <em>Headless CMS</em>: <a href="https://strapi.io/?ref=jorgetech.dev" rel="noreferrer"><em>Strapi</em></a> y <a href="https://directus.io/?ref=jorgetech.dev" rel="noreferrer"><em>Directus</em></a>. Dos espectaculares opciones que llegan hasta ese punto, el de Headless.</p><p>Estas 2 opciones poseen todo lo que uno necesita para administrar contenido: manejo de usuarios, permisos, documentos, im&#xE1;genes, rutas, etc. Sin embargo, como su nombre indica, son <a href="https://aws.amazon.com/es/what-is/headless-cms/?ref=jorgetech.dev" rel="noreferrer"><em>Headless</em></a>, o lo que en la practica traduce, s&#xF3;lo es el backend o la administraci&#xF3;n del contenido. Para mi soluci&#xF3;n, quer&#xED;a desplegar usando un solo proyecto o repositorio, y a pesar de que pod&#xED;a iniciar un proyecto en <em>React.js</em> o <em>Next.js</em> con alguna librer&#xED;a como <a href="https://mui.com/?ref=jorgetech.dev" rel="noreferrer">MUI</a>, no quer&#xED;a llegar hasta ese punto o complicaci&#xF3;n.</p><p>Otro punto a tomar en cuenta, es que en el trabajo est&#xE1;bamos evaluando un cliente el cu&#xE1;l insist&#xED;a con un CMS completo, que a su preferencia, quer&#xED;a usar WordPress. Desde el departamento sabemos que estas tecnolog&#xED;as ahorran en esfuerzo a la hora del despliegue, pero dificultan (y en gigantes rangos) el proceso de integraci&#xF3;n y crecimiento propio de la soluci&#xF3;n, pero en nuestra opinion, <strong>esta es la principal raz&#xF3;n por la que a&#xFA;n no logramos superar PHP como lenguaje: proyectos completos como WordPress o Drupal nos enamoran para su uso, alej&#xE1;ndonos de lenguajes de programaci&#xF3;n m&#xE1;s eficientes, &#xF3;ptimos y con mejores curvas de aprendizaje</strong>.</p><p>Nunca hab&#xED;a escuchado Ghost, y a pesar de que al inicio tuve algunas confusiones acerca de c&#xF3;mo resuelve el contenido, me gust&#xF3; saber que:</p><ul><li>Es un CMS que permite generar p&#xE1;ginas y posts.</li><li>Posee una interfaz completa para la creaci&#xF3;n y edici&#xF3;n de contenido.</li><li>Est&#xE1; enfocado a p&#xE1;ginas por suscripci&#xF3;n, sin embargo, esto es opcional.</li><li>Posee Newsletter, lo cu&#xE1;l permite crear campa&#xF1;as de correos personalizadas.</li><li>La interfaz est&#xE1; impulsada por Handlebars, lo cu&#xE1;l es una tecnolog&#xED;a f&#xE1;cil de conocer.</li><li>Existen varios temas gratuitos y otros m&#xE1;s que pueden adquirirse por un precio, que complementan y mejora todo lo que Ghost puede hacer.</li><li>Las rutas, lenguajes, tags y otros elementos pueden personalizarse.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-3.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1523" height="910" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-3.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-3.png 1000w, https://jorgetech.dev/content/images/2024/04/image-3.png 1523w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Portal de Ghost.</span></figcaption></figure><p>Y queriendo usar alg&#xFA;n lenguaje de f&#xE1;cil uso para mi como node.js, est&#xE1; fue una elecci&#xF3;n f&#xE1;cil que me entusiasm&#xF3;. Pero igual, les recomiendo tomar en cuenta las contras:</p><ul><li>Manejo de un s&#xF3;lo Content-Type (posts).</li><li>No posee integraci&#xF3;n nativa con servicios de archivos o <em>media files</em> como AWS S3.</li></ul><p>De todas maneras, sigo pensando que es una buena soluci&#xF3;n para portales sencillos: blogs personales, p&#xE1;ginas de empresas, informaci&#xF3;n de podscasts o eventos, etc.</p><h2 id="elecci%C3%B3n-de-aws-tambi%C3%A9n-personal-puedes-continuar-con-la-siguiente-secci%C3%B3n">Elecci&#xF3;n de AWS (tambi&#xE9;n personal, puedes continuar con la siguiente secci&#xF3;n)</h2><p>Este es un tema un poco m&#xE1;s directo que la elecci&#xF3;n de Ghost. Durante mi carrera, he trabajado con uso de c&#xF3;mputo en la nube con los proveedores Azure y AWS. Cada uno tiene sus pros y contras, sin embargo, en mi d&#xED;a a d&#xED;a actual, es m&#xE1;s frecuente la elecci&#xF3;n de AWS debido a que su principal ventaja es la econom&#xED;a de precios (o eso aparenta).</p><p>Sin ahondar mucho, me gusta probar y saber qu&#xE9; y qu&#xE9; no se puede hacer con este servicio de c&#xF3;mputo en la nube, y con el ofrecimiento de 750 horas de c&#xF3;mputo gratis durante un a&#xF1;o, me parece una buena opci&#xF3;n.</p><p>El portal de Ghost ofrece un servicio de despliegue de tu portal (<a href="https://ghost.org/pricing/?ref=jorgetech.dev" rel="noreferrer">TryGhost</a>), con tu propio dominio, por $ 9.00, el cual es un buen precio a mi parecer, pero sigo pensando en que quiero desplegar mi propia soluci&#xF3;n.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-2.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1518" height="885" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-2.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-2.png 1000w, https://jorgetech.dev/content/images/2024/04/image-2.png 1518w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Opciones ofrecidas de servicios de Ghost en la nube.</span></figcaption></figure><p>Como dije al inicio, quer&#xED;a hacer un despliegue desde sus inicios, usar las tecnolog&#xED;as de AWS y aprovechar las 750 horas ofrecidas, por lo que de una vez tom&#xE9; esta opci&#xF3;n.</p><h2 id="primeros-pasos-y-fallos">Primeros pasos y fallos</h2><p>Bien, ya pong&#xE1;monos en modo programador y despleguemos nuestro propio portal.</p><p>Antes de iniciar, debo adquirir mi propio nombre de dominio o DNS (Domain Name System), que es c&#xF3;mo voy a ser conocido en el internet. Me dirig&#xED; a Namecheap y adquir&#xED; jorgetech.dev por la m&#xF3;dica suma de aproximadamente $ 11.00.</p><h3 id="crear-mi-primera-instancia-de-aws-ec2">Crear mi primera instancia de AWS EC2</h3><p>Listo, ahora simplemente creo una instancia en AWS EC2 con una imagen en Ubuntu y hago la instalaci&#xF3;n. Todos los pasos se encuentran en la p&#xE1;gina de Ghost. Ya hab&#xED;a &quot;jugado&quot; con esta tecnolog&#xED;a en local, por lo que simplemente era instalar en mi nueva m&#xE1;quina virtual, subir el tema de mi elecci&#xF3;n (desde la tienda o personalizado) y listo, &#xBF;suena f&#xE1;cil no?</p><p>En primer lugar, acced&#xED; a mi cuenta de AWS, me dirig&#xED; a la secci&#xF3;n de EC2 e inici&#xE9; una instancia micro (permitida para las 750 horas gratuitas) usando la imag&#xE9;n de Ubuntu que hab&#xED;a mencionado. En menos de 5 minutos, ya pod&#xED;a conectarme a trav&#xE9;s del protocolo SSH a mi m&#xE1;quina virtual (recuerden crear las credenciales necesarias para la conexi&#xF3;n).</p><p>Listo, entonces es solo ingresar y seguir los pasos:</p><ol><li>Crear un nuevo usuario.</li><li>Actualizar todos los paquetes.</li><li>Instalar NGINX.</li><li>Instalar MySQL.</li><li>Instalar Node.js.</li><li>Instalar Ghost-CLI.</li><li>Crear el directorio y habilitar los permisos para el usuario de la m&#xE1;quina.</li></ol><p>Y listo, ya solo falta el paso 8, instalar Ghost.</p><ol start="8"><li>Instalar Ghost con el comando <code>ghost install</code>.</li></ol><p>Aqu&#xED; es donde todo no correo como esperamos. Esta instalaci&#xF3;n nunca ocurre, la m&#xE1;quina virtual dura horas instalando Ghost hasta que se pierde la conexi&#xF3;n. Pens&#xE9; que era un problema de falta de memor&#xED;a f&#xED;sica (AWS EBS), por lo que repet&#xED; la creaci&#xF3;n de la instancia dejando a la m&#xE1;quina con 50 GB de memoria, pero a&#xFA;n as&#xED;, la instalaci&#xF3;n se congelaba.</p><h3 id="probando-con-otras-im%C3%A1genes-oficiales-de-aws">Probando con otras im&#xE1;genes oficiales de AWS</h3><p>Al momento de crear una instancia, tenemos otras opciones para la imagen, desde Linux, Debian, una versi&#xF3;n creada espec&#xED;ficamente por Amazon, hasta instalar Windows o MacOS.</p><p>Obviamente todos los intentos fueron con las distribuciones de Linux (donde los comandos no son siempre iguales), sin embargo, suced&#xED;a lo mismo: siempre se congelaba en el paso 4 o 5 de la instalaci&#xF3;n de Ghost.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/ghost-install.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1217" height="620" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/ghost-install.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/ghost-install.png 1000w, https://jorgetech.dev/content/images/2024/04/ghost-install.png 1217w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Mensajes del terminal al instalar Ghost. Aqu&#xED; es donde la instalaci&#xF3;n se congelaba.</span></figcaption></figure><p>Por lo que ya hab&#xED;a que recurrir a la mag&#xED;a del internet. Resulta que este problema, no era &#xFA;nico para mi. Como pueden ver en el post, varios usuarios reclamaban el mismo inconveniente, por lo que eran un mensaje en Github donde un usuario indicaba que este inconveniente era porque la m&#xE1;quina virtual s&#xF3;lo ten&#xED;a 1 GB de memoria RAM y se necesitaban m&#xED;nimo 2 GB. Esto era contradictorio a la p&#xE1;gina de Ghost, donde se indica que m&#xED;nimo, 1 GB de memoria RAM es suficiente.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-1.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1419" height="584" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-1.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-1.png 1000w, https://jorgetech.dev/content/images/2024/04/image-1.png 1419w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Captura de pantalla a fecha 9 de abril de 2024 donde se indican los requerimientos para instalaci&#xF3;n.</span></figcaption></figure><p>L&#xE1;stima, todo parec&#xED;a muy sencillo. Luego de esto, s&#xF3;lo quedaba la configuraci&#xF3;n de NGINX, el dominio y los certificados.</p><p>Esto fueron varios intentos, cada uno durando entre 1 hora y 3 horas. Al 3er d&#xED;a me rend&#xED; con esta opci&#xF3;n y empec&#xE9; a buscar alternativas.</p><p>Pude haber elegido Digital Ocean para el despliegue, o directamente adquirir el servicio desde la p&#xE1;gina de Ghost, sin embargo, quer&#xED;a insistir con esta opci&#xF3;n. Dudo mucho que hayan clientes peque&#xF1;os en mi trabajo para proponer esta opci&#xF3;n, pero quien sabe, nunca est&#xE1; de m&#xE1;s tener esto en el arsenal (al menos una landing page con redirecciones puede ser muy f&#xE1;cil para vender).</p><p>Y aqu&#xED; es cuando descubr&#xED; las bondades de desplegar una instancia usando im&#xE1;genes de terceros, como los el proveedor Bitnami, quien justamente para Ghost tienen una imagen de uso gratuito.</p><h2 id="tutorial-o-gu%C3%ADa-de-despliegue-de-ghost-en-aws-ec2-con-uso-de-una-imagen-de-bitnami">Tutorial o gu&#xED;a de despliegue de Ghost en AWS EC2 con uso de una imagen de Bitnami</h2><p>Vamos a seguir los pasos de manera general de como instalar Ghost en AWS usando una imagen de instancia de Bitnami:</p><ol><li>Ingresamos a nuestra cuenta de AWS, nos dirigimos a la secci&#xF3;n de EC2 y seleccionamos crear una instancia.</li><li>En la secci&#xF3;n de imagen, nos vamos a la secci&#xF3;n de im&#xE1;genes de terceros.</li><li>Buscamos Ghost de Bitnami y seleccionamos la que necesitamos.</li><li>Seleccionar un &quot;key pair&quot; para la instancia (para la conexi&#xF3;n a trav&#xE9;s de SSH).</li><li>Seleccionar un &quot;security group&quot; que permita conexiones desde cualquier IP (Anywhere).</li><li>Configurar unos 30 GB de storage EBS.</li><li>Esperamos por la finalizaci&#xF3;n del despliegue y luego nos vamos a los mensajes de Log de la instancia.</li><li>Buscamos la secci&#xF3;n donde se inicia el usuario y contrase&#xF1;a para ingresar. Este proceso es para obtener las credenciales para ingresar al aplicativo por primera vez: <a href="https://docs.bitnami.com/aws/faq/get-started/find-credentials/?ref=jorgetech.dev" rel="noreferrer">encuentra las credenciales de la aplicaci&#xF3;n</a>.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/get-started/?ref=jorgetech.dev" rel="noreferrer">Getting started</a> puedes obtener informaci&#xF3;n adicional de c&#xF3;mo ingresar a la base de datos o la configuraci&#xF3;n inicial de Apache.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/configuration/?ref=jorgetech.dev" rel="noreferrer">Configuration</a> hay informaci&#xF3;n adicional para otras configuraciones del servicio SMTP o Apache.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/administration/?ref=jorgetech.dev" rel="noreferrer">Administration</a> se encuentra la mayor porci&#xF3;n de informaci&#xF3;n para el servidor en Ghost.<ol><li><a href="https://docs.bitnami.com/aws/apps/ghost/administration/control-services/?ref=jorgetech.dev" rel="noreferrer">Esta sub-secci&#xF3;n</a> te muestra los comandos para iniciar, reiniciar o detener los servicios de Ghost.</li><li><a href="https://docs.bitnami.com/aws/apps/ghost/administration/configure-domain/?ref=jorgetech.dev" rel="noreferrer">Sub-secci&#xF3;n a seguir</a> para configurar el dominio del servidor.</li></ol></li><li><a href="https://docs.bitnami.com/aws/faq/administration/generate-configure-certificate-letsencrypt/?ref=jorgetech.dev" rel="noreferrer">En esta gu&#xED;a</a> se encuentra la informaci&#xF3;n para configurar los certificados SSL y el DNS a trav&#xE9;s de HTTPS.</li><li>Crear una Elastic IP desde AWS y asignarla a nuestra instancia de Ghost.</li><li>Tambi&#xE9;n se debe configurar el DNS (para mi caso us&#xE9; Namecheap en la compra de mi dominio). Este proceso es sencillo y directo. Cuando est&#xE9;s configurando el dominio, ve a la secci&#xF3;n de &quot;Advanced DNS&quot; para el dominio y crea un &quot;A Record&quot;. Selecciona &quot;Host&quot; como &quot;@&quot; e introduce el Valor de la Elastic IP que configuraste antes. Guarda esto y crea un segundo &quot;A Record&quot;, selecciona &quot;Host&quot; como &quot;www&quot; y introduce el Valor para que sea el mismo del Elastic IP que configuraste antes.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145638.png" width="1857" height="922" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-145638.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-145638.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-145638.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145638.png 1857w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145745.png" width="1089" height="666" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-145745.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-145745.png 1000w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145745.png 1089w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-150207.png" width="744" height="573" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-150207.png 600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-150207.png 744w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-151228.png" width="1831" height="873" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-151228.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-151228.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-151228.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-151228.png 1831w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152044.png" width="1792" height="900" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-152044.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-152044.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-152044.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152044.png 1792w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152842.png" width="1854" height="885" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-152842.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-152842.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-152842.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152842.png 1854w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Screenshots del proceso mientras se configura Ghost desde AWS.</span></p></figcaption></figure><p>Hay varios puntos y fuentes de informaci&#xF3;n adicional que dejar&#xE9; por ac&#xE1; para vuestro inter&#xE9;s:</p><ul><li><a href="https://github.com/bdeitte/ghost-on-aws?ref=jorgetech.dev" rel="noreferrer">Readme en Github</a> por el usuario <a href="https://github.com/bdeitte/ghost-on-aws/commits?author=bdeitte&amp;ref=jorgetech.dev" rel="noreferrer">bdeitte</a> donde explica el proceso de configuraci&#xF3;n de Ghost con AWS y Namecheap.</li><li><a href="https://stackoverflow.com/questions/77107313/installing-ghost-cms-self-hosting-on-aws-ec2-seems-to-never-complete?ref=jorgetech.dev" rel="noreferrer">Consulta en Stack Overflow</a> donde se concluye que para la instalaci&#xF3;n de Ghost se requieren 2 GB de memoria RAM en vez de 1 GB.</li></ul><p>Se que hay otros datos importantes, sin embargo, si presentas un inconveniente durante la instalaci&#xF3;n, d&#xE9;jame saber para ayudarte o guiarte durante el proceso.</p>]]></content:encoded></item><item><title><![CDATA[Próximamente]]></title><description><![CDATA[<p>Este es mi blog, de parte de Jorge. Es un nuevo lugar para todos esos temas, pensamientos, tutoriales, ejercicios, pruebas de concepto e historias que quiero compartir sobre la programaci&#xF3;n de aplicativos web, softwares, tecnolog&#xED;as y dem&#xE1;s.</p><p>Soy programador de aplicativos web, por lo</p>]]></description><link>https://jorgetech.dev/proximamente-jorgetech/</link><guid isPermaLink="false">65d1690e301abf0719e62299</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Sun, 18 Feb 2024 02:18:54 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/proximamente.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/proximamente.png" alt="Pr&#xF3;ximamente"><p>Este es mi blog, de parte de Jorge. Es un nuevo lugar para todos esos temas, pensamientos, tutoriales, ejercicios, pruebas de concepto e historias que quiero compartir sobre la programaci&#xF3;n de aplicativos web, softwares, tecnolog&#xED;as y dem&#xE1;s.</p><p>Soy programador de aplicativos web, por lo que hay muchas ideas que quiero que conozcan y tal vez si uno o varios aprenden algo al respecto, me sentir&#xE9; m&#xE1;s que conforme. Tambi&#xE9;n quiero mostrar de lo que soy capaz y tener un lugar donde demostrar todos mis conocimientos.</p><p>Esto apenas est&#xE1; empezando, as&#xED; que prep&#xE1;rate y recuerda, si quieres ver alg&#xFA;n tema en espec&#xED;fico, s&#xF3;lo d&#xE9;jame saber.</p>]]></content:encoded></item></channel></rss>