necudeco

Required Inputs in your forms

Como marcar rapidamente todos los elementos required usando solo CSS

Durante la implementacion de un proyecto es posible que algunos campos requeridos ya no lo sean tanto al final. Tambien es posible que en lugar de un * quieran incluir el texto required, o un icono de !, etc. Ir cambiando cada uno de los labels, olvidarte de cambiar uno … es mucho estres.

Vamos a mostrar una forma de añadir dicho elemento de nuestro codigo desde un archivo CSS.

Primero, tenemos nuestro formulario HTML

<form>
     <div class="form-field">
         <label>Nombre</label>
         <input type="text" name"nombre" required />
     </div>



     <div class="form-field">
         <label>Apellido</label>
         <input type="text" name"nombre" />
     </div>


     <input type="submit" value="Enviar" />

</form>

Con nuestro codigo CSS vamos a buscar los elementos .form-field que tengan un hijo con el attributo required y a esos .form-field vamos a marcar su hijo label.

.form-field:has(:required) label:after{
   content: " required";
   color: red;
   font-size: 0.5em;
}

BASH: Como ejecutar el comando cp y excluir ciertos subdirectorios

Quiero copiar todos los archivos de un directorio excepto algunos archivos en un subdirectorio, aprendamos como !

Cuando ejecutamos el comando cp

cp <origen> <destino>

Linux crea ( o modifica ) el archivo destino con el mismo contenido que el archivo origen. Este comando no funciona si <origen> es un directorio

cp <origen> <destino> -r

En ese caso debemos usar el flag -r para indicar que copie el directorio <origen> y todo lo que se encuentre en su subdirectorios. https://man7.org/linux/man-pages/man1/cp.1.html

Sin embargo, en este caso en particular debia copiar <origen> que tenia 30 subdirectorios, menos el directorio assets

Podria haber ejecutado el comando cp 29 veces. Y aunque esa tarea no era muy dificil, no me veia ejecutando a mano 34 comandos de cp mientras espero que termine cada uno para poder ejecutar el siguiente.

Para resolver este problema, debemos indicarle al comando cp cada archivo que queremos copiar y excluir a los que no deseamos.

El comando CP puede recibir UN ORIGEN o MULTIPLES ORIGENES entonces nuestro problema se vuelve este: “como obtengo la lista de archivos de un directorio ? “

ls -A

El comando LS nos devolvera la lista completa de los archivos de ORIGEN. https://man7.org/linux/man-pages/man1/ls.1.html Debemos solo retirar la carpeta no deseada

ls -A | grep -v “assets”

En esta concatenacion de comandos, el comando GREP retirara de los resultados aquellos archivos que esten en la carpeta assets/archivos https://man7.org/linux/man-pages/man1/grep.1.html

El siguiente paso es enviarle esa lista de archivos de ORIGEN al comando CP.

cp -r $( ls -A | grep -v “assets” ) /tmp/DESTINO

Cuando encerramos un comando en $( ) estamos ejecutando primero el contenido del parentesis y luego ese resultado se integra en el comando exterior.

Como excluir servicios con Docker Compose ?

El comando docker-compose up inicia todos los servicios de nuestro proyecto. Incluso servicios que no deseamos como herramientas ( drush, por ejemplo ) o servicios de mantenimiento esporadicos ( docker-compose run –rm ). Entonces como podemos evitar que ciertos servicios se inicien con docker-compose up ?

Existe una opcion en nuestro archivo de configuracion docker-compose.yml con la cual podemos indicar que algun(nos) servicios no esten disponibles al levantar nuestro proyecto.

Profiles en Docker Compose

Cuando declaramos un servicio dentro de nuestro archivo docker-compose.yml podemos establecer la opcion profiles y establecer a que perfiles de ejecucion pertenecera ese servicio.

version: ‘2’

services:
node:
image : node:14
volumes:
– .:/var/www/html
command: “npm start”

db:
image: mysql:5.7
volumes:
– ./mysql:/etc/mysql/conf.d/
– ./db_init.sql:/docker-entrypoint-initdb.d/init.sql

puppeteer:
profiles: [“puppeteer”]
build: ./puppeteer/
volumes:
– ./puppeteer/:/var/www/html/
– ./config:/var/www/html/config
command: “npm start”

En este archivo de configuracion, estamos asignando al perfil puppeteer al servicio puppeteer. Por lo tanto cuando ejecutemos el comando:

docker-compose up -d

Solo se iniciaran los servicios node y db, excluyendo el servicio puppeteer.

Si quisieramos ejecutar el servicio puppeteer debemos indicar el nombre del servicio

docker-compose run –rm puppeteer npm start

Subiendo 2 millones de archivos a la nube

En el contexto de la migracion de un sistema PHP desde un entorno local a servidores en AWS, sin embargo debemos subir mas de 220 Gb de informacion en archivos 100K

Para poder subir archivos a servidores en la nube suelo usar comandos como scp o rsync, los cuales realizan la transferencia de los archivos en una conexion segura. Esta solucion funciona relativamente bien cuando se sube hasta 1000 archivos, sin embargo mas alla de eso se puede empezar algunos problemas.

Velocidad de Transferencia, cuando se realiza la transferencia de un archivo, la velocidad de transferencia empieza a la menor velocidad posible y va aumentando la velocidad conforme dura la transferencia. Eso quiere decir, que se necesita un tiempo minimo para alcanzar la velocidad maxima posible. Un archivo muy pequeño ( 100K ) no permite alcanzar la velocidad maxima de transferencia.

Costo Administrativo, de todo el tiempo que dura realizar la transferencia de un archivo. Una pequeña parte se gasta en coordinaciones entre servidor y cliente, este tiempo no es afectado por el tamaño del archivo. Es decir gasto 5 milisegundos ya sea vaya a subir un archivo de 100K o suba un archivo de 100M. En archivos muy pequeños este tiempo administrativo se transforma en una parte muy importante del tiempo total de transferencia.

Con estos dos problemas, subir 2 millones de archivos de 100K me generaba un tiempo estimado de 1 semana en condiciones de red ideales.

En este escenario recorde mi primer proyecto que desarrolle en la universidad en el año 1999. ( El siglo pasado o el milenio pasado … como prefieran ) que era cortar un archivo grande en pequeños pedazos para guardarlos en disquettes 3 1/4 y llevarlos de las cabinas de internet a la casa.

Debia pasar de 2millones de archivos a apenas 220 archivos de 1G cada uno. Con 1G de tamaño por archivo la velocidad de transferencia seria mucho mayor, y el Costo Administrativo de la Transferencia seria despreciable.

Para lograrlo debia convertir los 2 millones de archivos a uno solo

tar -cf archivos.tar archivos/

Con este comando creaba un archivo unico con el contenido de la carpeta archivos. No realize ninguna compresion ya que el proceso de comprimir podria ser muy demandante en tiempo y CPU para 220 Gb. Ademas que al no ser archivos de texto esperaba que el porcentaje de ahorro no sea mayor al 15%.

Con mi nuevo archivo de 220G, la transferencia hubiera tomando ya no 2 semanas, sino tan solo 5 dias. Sin embargo al ser un archivo unico, se corria el riesgo de corrupcion del archivo o corte de la transferencia y habria q volver a subir todo el archivo … siendo esto un riesgo inaceptable.

Para reducir este riesgo, opte por dividir el archivo en 220 partes de 1G cada una. una transferencia de 1G me tomaba unos 10min aprox. Si alguna fallaba, solo debia repetir esa parte.

split –bytes=1G archivo.tar partes/

Con este comando, indicaba a mi Ubuntu que cortara el archivo.tar en tantas partes de 1G como fuera necesario y lo colocara en la carpeta partes.

El proceso de corte es un proceso que demora bastante … ya vamos 2 horas y contando. Generar cada parte toma un aproximado de 2 minutos, asi que seran unas 8 horas casi 😀

Por mientras se van generando, se puede ir subiendo los archivos por rsync o scp.

Una vez los archivos estan en el servidor debemos proceder a juntarlos

cat * > archivos.tar

Este comando reconstruye el archivo tar original de 220G uniendo todas las partes.

tar -xf archivos.tar archivos/

Este comando descomprime el archivo tar a todos los 2 millones de archivos y lo coloca en la carpeta archivos/

En total reducimos el tiempo de transferencia de 2 semanas a tan solo 4h de creacion del TAR + 8h de creacion de las partes + 40h de transferencia + 8h de union de las partes + 4h de descompresion del TAR = 64h un total de 3 dias.

Como generar o restaurar un backup de PostgreSQL ?

Realizar un backup de una base de datos ( en este caso PostgreSQL ) es un proceso obligatorio en cualquier sistema en produccion.

Diferentes eventos pueden generar la perdida de datos, como por ejemplo: falla de los discos duros, acceso no permitido a la base de datos, un bug en tus sistemas que corrompa los datos, error humano de gestion, etc. Sin una politica de backup adecuada, ese suceso podria ocasionar la perdida completa de tu proyecto.

En el caso de postgresql, son dos los comandos que nos van a permitir trabajar con backups:

  • pg_dump, se encarga de generar un archivo SQL con el estado actual de la base de datos
  • psql, se encarga de leer y procesar el archivo sql generado por pg_dump
pg_dump -U username -W db_database > db_database.sql

En el comando pg_dump le he indicado que se conecte a la base de datos db_database con el usuario username y genere el backup completo y lo guarde en el archivo db_database.sql

Este archivo db_database.sql puede ser almacenado en algun tipo de dispositivo para su posterior uso en caso de un emergencia, yo recomendaria un servicio como AWS S3

Para restaurar el backup en una base de datos podemos usar el comando psql:

psql -U username -W db_database < db_database.sql 

En el comando psql le indicamos que se conecte a la base de datos db_database con el usuario username y ejecute los comandos sql indicados en el archvio db_database.sql

IMAPCOPY, una herramienta para migrar cuentas de correo entre servidores

SPOILER, migrar cuentas de correo toma su tiempo. Depende de la cantidad de mails existentes en cada cuenta pero diria que entre 8 y 20 horas por cuenta de correo. Asi que preparense un cafe y relajense.

Cuando un cliente quiere salir de Godaddy y migrar a Bluehost, no solo hay que mover el sitio web … tambien debes mover todas sus cuentas de correo. Aca explicaremos como realizar esa migracion usando la herramienta IMAPCOPY.

Primero debemos instalar las herramientas:

sudo apt install imapcopy stunnel4

ImapCopy, no trabaja con protocolos cifrados. Es por eso que necesitaremos la herramienta stunnel4 para abrir un puerto de nuestra maquina y conectarlo con el puerto del servidor remoto. Durante este proceso stunnel4 es la que se encarga del cifrado de la comunicacion.

Editamos el archivo de configuracion /etc/stunnel/stunnel.conf

foreground = yes
debug = info 

[outlook-imap]
client = yes
accept = 127.0.0.1:9993
connect = outlook.office365.com:993
verifyChain = yes 
CApath = /etc/ssl/certs
checkHost = outlook.office365.com
OCSPaia = yes

Con esta configuracion estamos definiendo un servicio que abrira el puerto 9993 en nuestra PC local y lo conectara directamente con el puerto 993 de outlook.office365.com usando los certificados SSL definidos en nuestra PC.

Ejecutamos el servicio

sudo stunel /etc/stunnel/stunnel.conf

Dejamos el servicio corriendo en foreground y en otra terminal procedemos a realizar la configuracion de imapcopy. Creamos un archivo imapcopy.cfg

SourceServer 127.0.0.1
SourcePort 9993

DestServer box2024.bluehost.com
DestPort 143
DenyFlags "\Recent"


Copy "contabilidad@empresa.com" "passwordorigen" "contabilidad@empresa.com" "passworddestino"
Copy "info@empresa.com" "passwordorigen" "info@empresa.com" "passworddestino"

En esta configuracion indico que voy a copiar correos desde el servidor SourceServer ( que si recordamos esta apuntando a outlook.office365.com gracias a stunnel ) al servidor de bluehost.com

Y en las lineas Copy indicamos que cuentas de correo se van a copiar desde el servidor SourceServer hasta el servidor DestServer.

Y lo ejecutamos con el comando:

imapcopy -l 

ImapCopy leera cada carpeta del servidor de origen he ira copiando uno a uno los correos existentes. A una velocidad aproximada de 2 a 5 segundos por correo.

Yo tengo en mi cuenta personal, luego de varias limpiezas unos 15 mil correos. Eso es aproximadamente 21 horas.

ADVERTENCIA, el proceso no se puede cortar. No hay forma de continuar. Si se te va la luz, pierdes el internet, se te cuelga la PC debes borrar la cuenta destino y volver a empezar.

Como configurar una aplicacion aspnet como servicio en Ubuntu

Una vez desarrollada nuestra aplicacion en dotnet, e instalada en el servidor queda un ultimo paso. Como hacer que este permanentemente activa ? Esto lo lograremos creando un servicio en Ubuntu ( Linux )

Lo primero sera crear nuestro archivo de definicion de servicio

# nano /etc/systemd/system/dotnet-app.service

El nombre del servicio en este caso sera dotnet-app, pero podria ser cualquier otro.

Este archivo donet-app.service, tiene un formato .INI que debemos completar, asi:

[Unit]
Description=.NET Web App running on Ubuntu

[Service]
WorkingDirectory=/var/www/dotnet-app
ExecStart=/usr/bin/dotnet /var/www/dotnet-app/app.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-app
User=www-data

[Install]
WantedBy=multi-user.target

En este ejemplo, le estamos indicando a Linux en que carpeta se encuentra el aplicativo ( WorkingDirectory ), cual es el comando para ejecutar el aplicativo ( ExecStart ), que hacer si el servicio se cae ( Restart ) y que usuario deberia correr el servicio ( User )

El siguiente paso es habilitar el servicio, para esto ejecutamos el comando

# systemctl enable dotnet-app.service

Esto iniciara el servicio y se asegurara de iniciarlo cada vez que el servidor se reinicie.

Si queremos ver los logs generados por el aplicativo podemos usar el comando

# journalctl -fu dotnet-app.service

Como instalar una aplicacion aspnet en Ubuntu Linux

Configurar drush con drupal y docker-compose

Drupal es un CMS muy potente para la creacion de sitios web o MVP de aplicaciones, sin embargo algunas operaciones de mantenimiento son mas sencillas de realizar desde la consola de comandos o directamente ser automatizadas en el servidor. Para poder realizar estas funciones utilizamos drush, y en este articulo aprenderemos a usarlo con docker-compose.

Docker-compose

Docker compose es una herramienta para configurar entornos de desarrollo o produccion a traves de un archivo de definicion YML. En este archivo podemos definir toda la infraestructura de servidores que se necesita para un proyecto en especifico, en este particular para un sitio drupal.

Por ejemplo, este seria un archivo docker-compose.yml para drupal

version: '2'

services:
  db:
    image: mysql:5.7
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=true

  drush:
    image: drush/drush:7
    volumes:
       - wwwroot:/app/
       - ./:/app/sites/default/

  web:
    image: drupal:7
    volumes:
      - wwwroot:/var/www/html/
      - ./:/var/www/html/sites/default/
    ports:
      - 80:80
    depends_on:
      - db
    stdin_open: true
    tty: true

volumes:
  wwwroot: {}

En este archivo, definimos 3 servicios:

  • DB, aca iniciara el motor de base de datos necesario para que drupal se conecte.
  • WEB, aca iniciara el servidor apache con una instalacion base de drupal
  • DRUSH, este servicio no es ejecutable, sino que nos permite ejecutar el comando drush en la misma carpeta de codigo que usa el servicio WEB.

Para iniciar el servidor de drupal debemos ejecutar el comando

docker-compose up -d web 

Este comando nos permite arrancar el servicio WEB y el servicio DB ( el cual esta ligado con WEB depends_on )

Para ejecutar un comando drush, usamos el siguiente comando

docker-compose run --rm drush cc all 

Este comando iniciar una instancia del servicio DRUSH, ejecuta el comando drush cc all y elimina la instancia.

Como saber si una clave existe en un objeto javascript ?

Existen diferentes alternativas de codigo para determinar si una clave existe en un objecto javascript. Vamos a conocer alguna de ellas, y vamos a medir cual es mas rapida.

Partimos del hecho que tenemos esta objeto en javascript

let obj = { a: 2, b: 5, c: [0,1,3,4] };
MetodoCodigoTiempo ( ms )
Operador IN“a” in obj0.099
hasOwnPropertyobj.hasOwnProperty(“a”)0.007
undefinedobj[“a”] == undefined0.002

El resultado mas rapido se obtuvo comparando el valor con la constante undefined.