Backup de base de datos PostgreSQL

Hacer copias de seguridad de una base de datos, es una tarea básica que cualquier persona que administre servidores debería conocer.

Y en el caso de necesitar hacer un Backup de una base de datos Postgres, es una tarea sencilla que se puede realizar con un simple comando:

$ pg_dump -U usuario -d base_de_datos > destino.sql

Solo será necesario reemplazar el texto resaltado por los parámetros de su DB.

Al ejecutar el comando, se le pedirá la contraseña del usuario de la DB y al finalizar su ejecución debería generarse un archivo .SQL que podrá ser importado sin problemas.

¿Como importar una base de datos?

Para importar una base de datos, primero sería necesario logearse con el usuario de postgres.

Por ej. con el comando:

# su postgres

y luego ejecutar:

$ psql -U usuario -d base_de_datos < origen.sql

Y listo!

Actualización en API de palabras al azar

En la API pública de palabras al azar, hoy se agregaron algunas mejoras.

La más importante de ellas, es el agregado de un nuevo parámetro ‘l’ que permite definir la longitud de las palabras a ser devueltas por la misma.

La otra modificación se trata de una corrección sobre el filtrado de caracteres.

Ya que en el dataset se podían llegar a encontrar algunos caracteres extraños y saltos de línea que no habían sido filtrados.

También ya no se devuelven letras en minúsculas, por lo que no existen dos o más variaciones de la misma palabra.

Por lo que ahora un ejemplo de llamada a la API con ambos parámetros sería el siguiente:

https://clientes.api.greenborn.com.ar/public-random-word?c=9&l=8

Que debería retornar 8 palabras al azar, con una longitud de 8 caracteres.

Archivo de configuración básico Nginx

Luego de meditarlo por un tiempo, decidí finalmente reemplazar Apache por Nginx.

Se podría decir que finalmente he sido convencido por su promesa de tener un mejor rendimiento que Apache.

Cuestión que parece ser verdad.

Aunque eso no significa que pueda volver a Apache si el mismo mejora en dicho apartado, es el servidor web con el cual empecé a trabajar y aún le tengo cierto cariño.

Además con la actualización a PHP 8 y su módulo PHP-FPM, la verdad que estoy muy satisfecho con su rendimiento.

El archivo de configuración

Aquí agrego una muestra de un archivo de configuración básico que uso para el sitio, claramente Ud. deberá modificar los parámetros para que se adecué a sus necesidades

server {
        root /RUTA/DIRECTORIO/SITIO/WEB;
        index index.php index.html;
        server_name DOMINIO.DE.SU.SITIO.COM.AR;

        location / {
                try_files $uri $uri/ @rewrite_url;
        }

        location @rewrite_url {
                rewrite ^/(.*)$ /index.php?viewpage=$1 last;
        }

        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
        }

        location ~* .(woff|eot|ttf|svg|mp4|webm|jpg|jpeg|png|gif|ico|css|js)$ 
        {
             expires 365d;
        }

        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /RUTA/CERTI/fullchain.pem; # managed by Certbot
        ssl_certificate_key /RUTA/CERTI/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = DOMINIO.DE.SU.SITIO.COM.AR) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    server_name DOMINIO.DE.SU.SITIO.COM.AR;
    listen 80;
    return 404; # managed by Certbot
}

DOMINIO.DE.SU.SITIO.COM.AR: Deberá ser reemplazado por el dominio del sitio (se puede especificar sub-dominio)

/RUTA/DIRECTORIO/SITIO/WEB: Deberá ser reemplazado por la ruta en la cual se halle su sitio web.

Este archivo de configuración está recomendado para cualquier sitio basado en PHP, como bien podría ser una web desarrollada con WordPress.

Precondiciones

En este caso se asume que:

  • Ud. tiene acceso SSH para poder configurar el servidor.
  • El sistema operativo es Debian o alguno de sus derivados.
  • Usa certificado SSL gratuito de Let’s Encrypt: https://letsencrypt.org/
  • Tiene correctamente configurado su firewall tieniendo abierto los puertos 80 y 443.

Comentarios sobre secciones del archivo

Para que se realice la reescritura de URL (que en Apache se realizaba habilitando el mod_rewrite y configurando la reescritura en el .conf del sitio o en el .htaccess) se usa la sección:

location / {
    try_files $uri $uri/ @rewrite_url;
}

location @rewrite_url {
    rewrite ^/(.*)$ /index.php?viewpage=$1 last;
}

Se especifica el uso de PHP-FPM con las líneas:

location ~ \.php$ {
     include snippets/fastcgi-php.conf;
     fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
}

Dependiendo de sus sistema operativo, puede que varíe la ruta especificada en el parámetro: fastcgi_pass.

La configuración referente al cacheo de archivos se especifica en:

location ~* .(woff|eot|ttf|svg|mp4|webm|jpg|jpeg|png|gif|ico|css|js)$ 
{
      expires 365d;
}

Configuración de certificado SSL y uso de HTTP2

La configuración del certificado SSL, en este caso, se realiza de forma automática utilizando el certbot de Let’s Encrypt; puede ver como instalarlo en: https://www.linode.com/docs/guides/enabling-https-using-certbot-with-nginx-on-debian/

En el archivo de configuración indicado, todo el contenido grisado y en itálica (a partir de la linea listen 443 ssl http2; # managed by Certbot inclusive) debería no ser incluido en el archivo, ya que deberá ser generado por el certbot.

A esta altura, se asume que ya habilitó el sitio agregando el correspondiente enlace simbólico.

Por ej usuando un comando similar al siguiente:

# ln -s /etc/nginx/sites-available/mi.sitio.com.ar /etc/nginx/sites-enabled/mi.sitio.com.ar

Y que agregó el correspondiente registro de tipo A en la configuración de registros DNS de su dominio.

Por lo que el siguiente paso será reiniciar Nginx para que se apliquen los cambios, con:

# service nginx restart

Si, todo va bien, no debería indicarse ningún mensaje en consola.

Y luego ejecutar el certbot con:

# certbot

Allí mismo se indicarán una o varias opciones de acuerdo a los dominios y subdominios de los sitios alojados en el servidor, dicha lista se arma de acuerdo a los archivos de configuración de Nginx.

Se elige la opción correspondiente al dominio o subdominio a configurar, y si todo va bien, el certbot genera el certificado SSL y actualiza el archivo de configuración del vhost.

Si no se quiere usar HTTP2 ya estaría todo casi listo, pero por defecto dicho protocolo no está habilitado, por lo que deberá volver a abrir el archivo de configuración, buscar la línea listen 443 ssl; # managed by Certbot y reemplazarla por: listen 443 ssl http2; # managed by Certbot

Ya para finalizar será necesario volver a reiniciar el servicio nginx.

Generador de contraseñas basado en palabras

En el proyecto del generador online de contraseñas, ya se agrega la posibilidad de generar nuevas a partir de la API pública de Greenborn de palabras al azar.

Puedes ver más información sobre la misma en: https://blog.greenborn.com.ar/2022/02/28/api-publica-de-palabras-al-azar/

Se agregaron las siguientes mejoras

  • Modificación del código hacia la programación orientada a objetos: Se mejora la programación del componente para poder hacerlo más fácil de mantener.
  • Se agrega posibilidad de generar contraseñas a partir del servicio de palabras al azar.
  • Se agrega imagen de fondo.

Podes usarlo en: https://demos.greenborn.com.ar/pass-generator/

Repositorio: https://github.com/Greenborn/generador_contrase-as_web

API Pública de palabras al azar

Bien, anteriormente he desarrollado un pequeño proyecto de generador de contraseñas, que surgió con la necesidad de utilizar una herramienta propia para dicho propósito y brindar un servicio más en el servidor de Greenborn.

Y la siguiente mejora de dicho proyecto consiste en la generación de contraseñas basadas en palabras.

Para hacer posible dicha mejora se hace necesario contar con una base de datos de palabras (en principio en español) y la posibilidad de consultar una al azar.

Claro que también se podría utilizar para otros propósitos.

Y ya que la API necesitaría contar con un end-point público, creo que también estaría bueno brindar la documentación necesaria para hacer posible su uso por parte de cualquier persona, para el propósito que considere necesario.

¿Cómo se usa?

Su uso es muy sencillo, sólo hace falta hacer una petición GET de la siguiente URL:

https://clientes.api.greenborn.com.ar/public-random-word

A lo cual la petición respondería con una palabra al azar.

También se puede especificar si el resultado debe devolverse en JSON o XML de acuerdo al header content-type pudiendo definirse en application/json o application/xml.

Además si en la URL se agrega el parámetro ?c=x, siendo x un número entre 1 y 100, se puede especificar la cantidad de palabras a retornar.

Condiciones de uso y Limitaciones

  • No se ofrece ninguna garantía con respecto a cualquier tipo de aspecto del servicio ofrecido.
  • El usuario será responsable de cualquier tipo de uso que le de a la API, desligándose Greenborn de cualquier tipo de responsabilidad sobre el mismo.
  • El uso GRATUITO del servicio está limitado a un máximo de 500 consultas por hora, por dirección IP.
  • Cualquier aspecto de las condiciones de uso, podrá ser modificado sin previo aviso, al igual que no se garantiza la disponibilidad del servicio (salvo que acordemos el pago por cuota de uso).
  • Actualmente el dataset es reducido y se ampliará con el tiempo.

Ampliación del dataset

La base de datos de palabras no se cargará a mano, de hecho se realiza de forma semiautomática.

A partir de la carga de archivos de texto plano de libros en español, a lo cual se llegó alrededor de 40k palabras (existiendo variaciones que agregan mayúsculas y minúsculas).

En este caso los libros en español de donde se obtuvieron las palabras, provienen de: http://www.libroteca.net/

Mejora en Generador de Contraseñas

Anteriormente ya había programado un simple generador de contraseñas y desde el principio la idea es no abandonarlo e ir agregando mejoras a medida que pasa el tiempo, de acuerdo a las posibilidades.

Por lo que hoy se agregaron las siguientes actualizaciones:

  • Mejora en presentación de botones y texto informativo: Ahora se usan los botones propios de Bootstrap y se le asignan colores de forma que sean más fáciles de identificar.
  • Se agrega posibilidad de modificar los símbolos, números y letras utilizados en la generación de contraseñas.
  • Se agrega enlace de sistema de reporte de errores: El enlace redirige a un sistema propio de Greenborn en el cual se sistematizan los diferentes bugs detectados por los usuarios.

Posibles mejoras a futuro

Algo que estoy pensando agregar en un futuro cercano, es la posibilidad de generar contraseñas a partir de palabras concatenadas.

De forma que se podría generar una contraseña que ademas de ser larga, pueda ser fácil de recordar.

Por ej. es claro que una contraseña al estilo: “RUYI6Bw41}e#X6W8p2&s33l6w@jG64Bs/26~” es mucho más difícil de recordar que otra al estilo: “CEIBO.GUAYMALLEN.PILI.BARCO.TAMIKÉN.PARLA”.

¿Que tan segura son las contraseñas basadas en palabras?

En este caso, la combinatoria estaría de nuestro lado si las palabras se eligen desde una gran base de datos.

Por ej. si se tuvieran registradas 10.000 palabras, y se quisieran saber todas las posibles contraseñas a generar con:

  • 1: 10.000 posibilidades.
  • 2: 100.000.000 posibilidades.
  • 3: 1.000.000.000.000 posibilidades.
  • 5: 100.000.000.000.000.000.000 posibilidades.

Básicamente la cantidad de combinaciones se calcula de la forma:

#pass = #palabras_elegibles ^ cantidad_palabras

Siendo #pass la cantidad máxima de combinaciones, #palabras_elegibles la cantidad de palabras disponible en la base de datos elevado a la longitud de la contraseña expresada en cantidad_palabras.

Adicionalmente se podría definir la posibilidad de crear una contraseña mixta, es decir una en la cual se puedan intercalar palabras con las opciones anteriores ya definidas.

No se trata de ningún invento

Las contraseñas basadas en palabras, se usan mucho por ej. en las billeteras de cripto monedas (Wallets) como una de las opciones de recuperación de la cuenta, ya que si de diera el caso de que se borrara la configuración del wallet se pudiera recuperar su acceso a partir de dicha clave.

Y también es claro que se pueden usar en otros sistemas que requieran el uso de contraseñas, aunque tal vez su mayor limitante sea la longitud del texto generado, ya que existen páginas que limitan la longitud de las claves, y en otras ocasiones el límite no siempre es explicito.

Prueba de iluminación dinámica 2D

Hace un tiempo atrás, me interesé por el uso de iluminación dinámica 2D que sería utilizada como un recurso en la programación de juegos HTML5.

Claro, que hay varias opciones, pero la idea principal también es la de aprender mientras se hace, por lo que me propuse a realizar un motor de iluminación dinámica 2D.

Bueno la palabra “motor” a lo mejor le queda muy grande, más bien se trata de un experimento.

El ejemplo en cuestión

Puedes ver el código en: https://github.com/2012lucho/phaser-2d-dinamic-lights

Aquí mismo agrego un ej del mismo en funcionamiento:

En dicha demo, definí dos luces que permanecen inmóviles en su posición, una de ellas es de color verde la otra celeste; adicionalmente agregué tercera luz de color amarillo que se mueve siguiendo la posición del mouse.

¿Como generar el efecto de sombras?

Buscando la manera más sencilla y que a su vez consuma la menor cantidad de recursos posible, se me ocurrió que una buena forma de simular las luces y sombras sería el de utilizar una nueva imagen superpuesta, la cual funciona como si se tratara de una máscara (se le llamará así de ahora en adelante).

Inicialmente dicha imagen debería ocupar el tamaño total de la zona visible y estar siempre por encima de todos los objetos de la escena.

También todos sus pixeles serían negros y con la opacidad al 100%.

Luego se simula el trazado de los rayos de luz de cada una de las luces, de forma secuencial, de forma que cada una de ellas se dibuja en orden.

En cada posición en donde se determina que pasa el rayo de luz, se modifica el pixel correspondiente de la máscara redefiniendo su color y su valor de opacidad, de forma que dicho pixel ahora tenga cierto grado de transparencia, permitiendo ver así los objetos que se encuentran por debajo.

En las posiciones en donde los rayos de luz se superponen, se realiza la suma de sus colores en proporción a un valor correspondiente a la intensidad que tendría la luz en dicha parte del recorrido, para imitar su comportamiento natural.

También hay que tener en cuenta que no pretende ser una simulación 100% realista.

¿Por que se ve tan pixelado?

Seguro sea una de las primeras cosas que salta a la vista, bueno eso se debe a la forma en la cual funciona, por que para cada fotograma se deben hacer miles de cálculos para simular los trazos que realizarían los rayos de luz.

Esto si se hace en una GPU no supondría gran problema ya que están pensadas para ello, pero en este caso los cálculos los realiza un único núcleo de CPU.

Por lo que la forma más fácil que se me ocurrió, para que el sistema de iluminación no consuma grandes recursos, es el de bajar la resolución con respecto al cálculo de luces.

Por lo que la máscara en este caso es 5 veces menor que el área visible, y luego se re-dimensiona para que ocupe la totalidad de la superficie.

Así como también se redució la resolución de la simulación, también se puede definir que la misma no se realice en todos los fotogramas, de forma de minimizar aún más la carga de trabajo.

¿Y los obstáculos?

Sin obstáculos, la luz avanza en linea recta hasta el infinito, en este caso avanza hasta salirse de la pantalla o hasta chocar con un obstáculo.

Por lo que se hace necesario también definir una especie de “mapa de durezas”, por lo que se define una matriz del mismo tamaño que la cantidad de píxeles de la máscara.

Y la idea principal, es que todos aquellos objetos del juego que interactúen con la luz actualicen el mapa de durezas.

Indicando si se trata de un pixel opaco o no, aunque también se podría mejorar de forma de definir grados de transparencia, indice de refracción, etc.

Hay que desmitificar la Consola/Terminal/Linea de Comandos

Pareciera que cuando se habla de ejecutar comandos en una computadora, fuera cosa de hackers, gente que sabe mucho, que quiere hacer las cosas más complicadas de lo que son, o cualquiera de las múltiples interpretaciones, que en general tienden a que usar esa herramienta sea algo percibido como complejo.

Y puede que mucha gente ni se interese en ella por que lo más lógico es no complicarse sin necesidad.

Cuando en realidad no es tan así.

Solo una forma más de interactuar con la PC

Así como está el mouse para clickear el programa que se quiera abrir, también está la terminal en donde esa misma acción se puede hacer escribiendo el nombre del programa y apretando la tecla Enter, y con la función de auto-completado además puede que ya aparezca el nombre escribiendo las primeras 3 o 4 letras.

También se puede navegar entre los archivos y carpetas, aunque de una forma diferente, con el comando “cd” (abreviación de Change Directory), y con la función de auto-completar (apretando Tab) se termina de escribir parte del nombre de la ubicación de la carpeta a donde se quiera ir, y si uno ya se acuerda la ubicación, hace que moverse entre carpetas pueda hacerse de forma muy rápida.

Claro que hay que acordarse de memoria algunos comandos básicos, que en realidad se cuentan con los dedos de la mano. unos para ver el contenido de la carpeta en la cual se está situado (ls, dir) otros para copiar y mover archivos (cc, mv), y así con el resto de funciones, ya después de un tiempo de usarlos ya sale naturalmente.

A veces no queda otra

Muchos sistemas, por diferentes motivos, solo pueden ser usados usando una terminal, en general se tratan de servidores, equipos de red como routers, microcontroladores, algunos tipos de equipos especializados, etc.

Además que muchas cosas muy específicas son más prácticas de hacer con comandos.

Como por ej. si se necesitara renombrar muchos archivos que cumplan con criterios específicos, entre las muchísimas posibles tareas.

Que puede que le de parte de su reputación al ser una herramienta de trabajo más para el informático.

También siempre aparece en las películas de hackers, y a veces las escenas son muy fantasiosas haciendo que parezca algo más épico.

Y exclusivo de quienes saben de computadoras.

A primera vista espanta

Una pantalla negra con letras blancas sin nada más claramente no es algo amigable, algo así como una barrera o un peaje que se puede pasar sabiendo la frase secreta.

Además es algo viejo, de las computadoras de hace como 40 años atrás, esas que ahora están súper amarillas por los años.

Y si, esas computadoras usaban la terminal, en la época del DOS era la única forma de comunicarse con el sistema.

Hasta que Xerox desarrolló una de las primeras interfaces gráficas que agregaron el mouse, los íconos, el escritorio, etc.

O se quiere o se le odia

Si ya se usa durante un tiempo, puede ser que a lo mejor pasé que uno no se termine de habituar y desista para no volver nunca más a usarla.

O que uno se pueda acostumbrar y encontrarle el gusto, por que con suficiente tiempo de uso se pueden hacer algunas cosas mucho más rápido.

Igual, dependiendo de que se puede ahorrar mucho tiempo como también ninguno, y puede ser que lo cómodo al final sea lo que diga la costumbre.

Comando SCP – Transferir archivos entre equipos

Una de las formas más fáciles de transferir archivos entre servidores es por medio de SSH, y hay un comando que nos puede ayudar para eso, resolviéndolo de forma ágil.

Por lo que a continuación se detallarán diversos ejemplos de uso.

Ejemplos de uso

Se necesita transferir un archivo local a un servidor:

El server usa el puerto “888“, se requiere de una clave “clave.pem” y se hará login con el usuario “usuario_remoto“.

$ scp -P 888 -i clave.pem /ruta/archivo/local.zip usuario_remoto@server:/ruta/archivo/remoto.zip

Se necesita transferir un archivo desde el servidor a una ubicación local

Usando los mismos parámetros del ejemplo anterior:

$ scp -P 888 -i clave.pem usuario_remoto@server:/ruta/archivo/remoto.zip /ruta/archivo/local.zip

Sobre los parámetros básicos

El número de puerto se puede obviar si es el 22 que es utilizado por defecto por el servicio SSH al igual que la clave.pem dependiendo del equipo al que se necesite conectar.

Generador de contraseñas en Angular

Una de las tareas que siempre se presenta es la generación de contraseñas seguras para todos los sistemas tanto en producción como en desarrollo, y usaré esa necesidad propia como escusa para crear un pequeño proyecto angular de generación de contraseñas, y al mismo tiempo ofrecer un servicio más en la web Greenborn.

Manos a la obra

Repo en GitHub

Lo primero que necesitaremos es un repositorio de Github, claramente no es estrictamente necesario pero es una buena practica mantener el código versionado y en este caso está bueno para a quien le interese chusmear el proyecto, si recién comienza a trabajar con dicho Framework.

Enlace a repositorio: https://github.com/Greenborn/generador_contrase-as_web

Prerequisitos

Antes de comenzar es necesario tener instalado:

  • NodeJS en su última versión estable.
  • NPM para la gestión de paquetes.
  • Angular-cli para el desarrollo del proyecto Angular, al proporcionar el comando ng
  • Git (opcional) para la gestión del repositorio.

Creación de proyecto base

El proyecto Angular base se deberá crear utilizado el comando ng de la siguiente manera:

$ ng new GeneraPass

Dicho comando preguntará si se desea utilizar el Router del Framework, a lo cual responderemos que si, ya que si bien puede que no lo necesitemos en un principio, luego puede ser que se necesite si se agregan varias páginas a la navegación.

Luego nos preguntará si deseamos usar CSS, SCSS, SASS o LESS, aquí no me complicaría mucho y elegiría CSS, igualmente cualquiera de las opciones anteriores son muy buenas.

Creación de proyecto angular satisfactoria

Bien, ya tenemos el proyecto generado, ahora solo resta probarlo, para lo cual es esencial ir al directorio del proyecto y ejecutar ng serve:

$ cd GeneraPass
$ ng serve

Si todo va bien, ya debería abrirse una nueva pestaña en el navegador predeterminado con el proyecto funcionando, si no es así hay que mirar en la terminal en que ubicación se sirve el proyecto, en general por defecto es: http://localhost:4200

Proyecto base funcionando correctamente

El comando ng serve se usa para montar un servidor de desarrollo el cual se encarga de actualizar el proyecto automáticamente, cada vez que se detectan cambios, de forma que siempre veremos la versión actualizada del proyecto.

Primeras modificaciones

Bien, ahora ya podremos modificar el proyecto a nuestra conveniencia

Lo primero que podremos hacer es cambiar el favicon, por el que utilizaríamos en el proyecto, para lo que hay que reemplazar el archivo:

src/favicon.ico

El siguiente paso es la eliminación del código del template base, osea la vista que vemos al iniciar el proyecto.

Para eso deberemos editar el archivo: src/app/app.component.html

En este caso borramos todo su contenido, por lo cual obtendremos una vista en blanco.

Elección de Framework de estilos

En este punto uno puede o no utilizar un Framework como Bootstrap, para el maquetado de las vistas, en este caso, la decisión es de utilizarlo, por lo cual lo agregaremos al proyecto con el siguiente comando:

$ ng add @ng-bootstrap/ng-bootstrap

Dicho comando ya nos instala todas las librerías necesarias, y nos deja todo listo para ya poder utilizar Bootstrap en las vistas!

Puedes ver el sitio oficial de ng-bootstrap en: https://ng-bootstrap.github.io

Otra librería que utilizaremos, será la de iconos de Bootstrap, la cual podremos instalar con el comando:

npm i ngx-bootstrap-icons

Recomiendo leer la documentación de la misma en: https://www.npmjs.com/package/ngx-bootstrap-icons

Una vez que la dependencia se haya instalado correctamente, procederemos a incluir la misma en el archivo: src/app/app.module.ts

Dicho archivo ahora debería verse así:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { GeneratePasswordComponent } from './components/generate-password/generate-password.component';
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
declarations: [
AppComponent,
GeneratePasswordComponent
],
imports: [
BrowserModule,
AppRoutingModule,
NgbModule,
FormsModule,ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

En dicho Módulo, también se puede ver que se importan los módulos FormsModule, ReactiveFormsModule que son necesarios para poder bindear los inputs con el modelo.

Creación del componente PasswordGenerator

Los componentes son la base de cualquier proyecto Angular, y lo ideal sería separar lo mejor posible cada una de las funcionalidades del proyecto en componente que luego se puedan reutilizar.

En este caso por motivos de simplicidad y por que el proyecto en si no es muy complejo, se creará un solo componente.

Para crear nuevos componentes, lo mejor es usar el comando especifico para dicha función:

$ ng g c components/GeneratePassword

En dicho comando el argumento “g” indica que se debe generar un nuevo elemento, “c” indica que dicho elemento es un componente y components/GeneraPassword es la ruta en la cual se debe generar el mismo.

Componente generado correctamente, puede verse que también se especifica la modificación del archivo src/app/app.module.ts

Ahora ya podremos incluir dicho componente en cualquier vista, en el nuevo archivo src/app/components/generate-password/generate-password.component.ts se especifica el selector “app-generate-password“.

Lo que significa que podremos incluir dicho componente en cualquier vista referenciandolo de la siguiente manera:

<app-generate-password></app-generate-password>

Por ej lo incluiremos en src/app/app.component.html

Que es el archivo en el cual anteriormente borramos todo su contenido, por lo que ahora solo debería contener:

<div class="container-fluid">
     <app-generate-password></app-generate-password>
</div>

Vista de la página, luego de agregar dicho componente.

Maquetado de la vista

Ahora ya podremos modificar la vista, la cual se encuentra en el archivo: src/app/components/generate-password/generate-password.component.html

La cual actualmente solo contiene el siguiente código:

<p>generate-password works!</p>

Y lo reemplazaremos por el nuevo código HTML:

<div class="row"> 
<div class="col col-sm-8 offset-sm-4 col-lg-6 offset-lg-3 col-xxl-4 offset-xxl-4"> <div class="row mt-5">
<div class="col text-center">
<h2 class="title p-2">Generador de Contraseñas</h2>
</div>
</div>

<div class="row">
<div class="col mb-5"> <p>Puede crear una nueva contraseña segura, con nuestra herramienta On-line, solo complete el formulario y se generarán diferentes contraseñas aleatorias.</p>
</div>
</div>
<div class="row">
<div class="col m-3">
<div class="row mb-4"> <div class="col password-cont p-0">
<input class="p-2" id="passCont" value="{{password}}" placeholder="*************" />
</div>
<div class="col-auto p-2">
<i-bs name="clipboard" class="app-btn" (click)="copyToClipboard()"></i-bs>
<i-bs name="arrow-clockwise" class="app-btn" (click)="generatePassword()"></i-bs>
</div>
</div>
<div class="row options-content">
<div class="col">
<div class="row">
<div class="col text-center">
<h4 class="p-2">Opciones de generación Contraseña</h4>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 mb-2">
<label for="longInput" class="form-label">Longitud</label>
<input [(ngModel)]="passwordOptions.long" type="number" min="0" class="form-control" id="longInput" placeholder="25">
</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 mb-2">
<div class="form-check">
<input [(ngModel)]="passwordOptions.mayus" class="form-check-input" type="checkbox" value="" id="mayusInput" checked>
<label class="form-check-label" for="mayusInput"> Mayúsculas </label>
</div>
</div>
<div class="col-12 col-sm-6 mb-2">
<div class="form-check">
<input [(ngModel)]="passwordOptions.minus" class="form-check-input" type="checkbox" value="" id="minusInput" checked>
<label class="form-check-label" for="minusInput"> Minusculas </label>
</div>
</div>
<div class="col-12 col-sm-6 mb-2">
<div class="form-check">
<input [(ngModel)]="passwordOptions.symbol" class="form-check-input" type="checkbox" value="" id="simbolInput" checked>
<label class="form-check-label" for="simbolInput"> Símbolos </label>
</div>
</div>
<div class="col-12 col-sm-6 mb-2">
<div class="form-check">
<input [(ngModel)]="passwordOptions.numbers" class="form-check-input" type="checkbox" value="" id="numberInput" checked>
<label class="form-check-label" for="numberInput"> Números </label>
</div>
</div>

</div>
</div>
</div>
</div>
</div> </div>
</div>

Sobre el HTML, resaltaría los [(ngModel)] que definen el binding entre los valores de los inputs y el modelo (a definir) en donde se almacenarían los valores del formulario.

Y los (click) que definen la llamada a la función definida en el mismo, que será ejecutada al producirse el evento onClick.

Definición de modelo

La configuración usada para la generación de la contraseña se almacenará en un nuevo modelo que crearemos a tal propósito.

Para lo cual, dentro del directorio del componente, se creará un directorio models y dentro del mismo un archivo: password.options.ts

Dentro del cual se agregaría el siguiente contenido:


export class PasswordOptions {
    public long:number = 8;
    public mayus:boolean = true;
    public minus:boolean = true;
    public symbol:boolean = true;
    public numbers:boolean = true;
}

Programación del componente

Luego deberemos editar el archivo correspondiente al código del componente: src/app/components/generate-password/generate-password.component.ts

Dicho archivo nos quedaría de la siguiente manera:


import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { PasswordOptions } from './models/password.options';

@Component({
  selector: 'app-generate-password',
  templateUrl: './generate-password.component.html',
  styleUrls: ['./generate-password.component.css']
})
export class GeneratePasswordComponent implements OnInit {

  constructor() { }

  public password:string = '';
  public passwordOptions:PasswordOptions = new PasswordOptions();

  private passCont!: HTMLInputElement;

  ngOnInit(): void {
  }

  copyToClipboard(){
    if (this.password == '') {
      alert('Para poder copiar una contraseña primero es necesario generar una nueva');
      return false;
    }

    this.passCont = document.getElementById("passCont") as HTMLInputElement;
    this.passCont.select();
    document.execCommand('copy');
    alert("¡Contraseña copiada al portapapeles!");

    return true;
  }

  generatePassword(){
    if (this.passwordOptions.long < 0){
      alert('La longitud de la contraseña debe ser mayor a 0');
      return false;
    }

    if (!(this.passwordOptions.minus || this.passwordOptions.mayus || this.passwordOptions.numbers || this.passwordOptions.symbol)){
      alert('Es necesario elegir al menos una opcion de tipo de contraseña');
      return false;
    }

    if (this.passwordOptions.long >= 10000){
      alert('La cantidad de caracteres ingresada es demasiado grande!');
      return false;
    }

    this.password = '';
    for (let c=0; c < this.passwordOptions.long; c++){
      this.password += this.getPasswordChar();
    }

    return true;
  }

  getPasswordChar():string{
    //se genera un numero al azar entre 1 y 4, dicho numero representa el tipo de caracter a insertar
    let option:number = Math.round( Math.random() * (4 - 1) + 1 );
    let ch:string     = '';
    
    switch (option){
      case 1:
        if (!this.passwordOptions.mayus) { //si no se seleccionaron las mayusculas
          return this.getPasswordChar();
        }
        return this.getCaracter(this.letras).toUpperCase();
      break;

      case 2:
        if (!this.passwordOptions.minus) { //si no se seleccionaron las minusculas
          return this.getPasswordChar();
        }
        return this.getCaracter(this.letras);
      break;

      case 3:
        if (!this.passwordOptions.numbers) { //si no se seleccionaron numeros
          return this.getPasswordChar();
        }
        return this.getNumero();
      break;

      case 4:
        if (!this.passwordOptions.symbol) { //si no se seleccionaron simbolos
          return this.getPasswordChar();
        }
        return this.getCaracter(this.simbolos);
      break;
    }
    return ch;
  }

  private letras:string = 'abcdefghijklmnñopqrstuvwxyz';
  private simbolos:string= '!$%&/()=?^_-:;@*º|#~{[]}';
  getCaracter(chs:string):string{
    let chsCount  = chs.length -1;
    let chsSelect = Math.round( Math.random() * (chsCount - 0) + 0 );
    return chs[chsSelect];
  }

  private getNumero():string{
    return String(Math.round( Math.random() * (9 - 0) + 0 ));
  }

}

Sobre el código, podría resaltar algunas cuestiones, como por ej el prestar atención al ámbito de las variables, es decir si son de tipo private, solo podran ser accedidas desde dentro del componente y no se podrá hacer binding con la vista, puede funcionar mientras se realizan pruebas con ng serve, pero al hacer el ng build va a saltar un error indicando la situación.

Definicón de estilos

Y el archivo CSS de nuestro componente (src/app/components/generate-password/generate-password.component.css):


.title{
    background-color: rgb(176, 255, 131);
    font-weight: bolder;
}

.password-cont{
    border: 1px solid #555;
}

.password-cont input{
    width:100%;
    border:none;
    color: rgb(0, 43, 22);
    font-weight: bolder;
}

.options-content{
    box-shadow: 0px 1px 6px #888;
    border-radius: 0.2rem;
}

Hay que tener en cuenta que los estilos definidos en este archivo solo se aplicarán al componente en cuestión, lo ideal es que en el mismo no haya nada que aplique a varios componentes, en esa situación los estilos que pudieran repetirse deberìan definirse el style.css.

En el archivo CSS de estilos global del template (src/styles.css) deberemos agregar:


.app-btn{
    cursor: pointer;
    padding: .5rem;
    border: 1px solid #555;
}

.app-btn:hover{
    background-color: rgb(161, 192, 255);
}

La clase app-btn se define en el archivo de estilos globales, por que se prevee que pueda ser utilizada en caualquier sección del código de la APP, y se utiliza para definir el comportamiento mínimo del botón en cuanto a lo visual, definiendo su borde, el cursor y el mouse hover.

Probando

Si no hay ningún error, deberíamos ver la siguiente pantalla en el navegador:

Y bien, ya estaría terminado nuestro generador de contraseñas con Angular!

Generando el build

El último paso sería hacer el build del proyecto, para tener lista la versión optimizada para producción, por lo cual bastará con ejecutar el comando:

$ ng build

El cual generá dicha versión dentro del directorio: dist

https://demos.greenborn.com.ar/pass-generator/