Construye una aplicación web descentralizada desde cero con Vue, Vite y Tailwind CSS desplegada con IPFS
A día de hoy existen muchas plataformas que permiten desplegar un sitio web estático, basado en jamstack, de forma sencilla y con un coste muy reducido o con una cuenta de usuario gratuita. Normalmente facilitan también la integración con sistemas de control de versiones y CI/CD, haciendo así viable su uso a nivel profesional.
Hablo de plataformas como Netlify, Vercel, Glitch, GitHub Pages o Firebase. Utilizar estos servicios es muy tentador, y en gran parte de los casos una opción válida, pero todas estas plataformas son servicios centralizados.
¿Por qué deberia de descentralizar mi web?
Los servicios centralizados a pesar de ser rápidos y baratos, tienen el inconveniente de que somos dependientes de ellos.
Estos servicios suelen ser software controlado por una empresa que se ejecuta sobre hardware de esa misma empresa u otra. Nuestra única forma de mantener un control sobre nuestros datos y acceso al servicio es legal, mediante contratos y términos y condiciones, ya que el poder y control reales están fuera de nuestras manos.
Un ataque informático o un administrador corrupto podría exponer nuestros datos. Los servidores de la plataforma que usemos podrían estar caídos. La empresa podría desaparecer y dejar de prestarnos servicio...
Y aunque tengamos derecho a pedir explicaciones e indemnizaciones por lo sufrido, el daño ya está hecho y puede ser irreparable.
En un sistema o red descentralizada, los usarios actuan como iguales y por lo general cada uno es responsable de sus datos y sus recursos a no ser que alguna regla del sistema especifique lo contrario. Brindan un mayor control pero también suponen una mayor responsabilidad.
Además tienen también sus desventajas. Son lentas comparadas con los servicios centralizados y en algunos casos pueden ser suponer un coste económico mayor o tener un impacto negativo en el medio ambiente.
Pero aunque algunas redes descentralizadas por cuestiones de seguridad tienen un alto impacto ambiental, IPFS (que es la opción que proponemos hoy) apunta justo en la dirección contraria. Propone una manera más inteligente, eficiente y resiliente de servir contenido que el protocolo HTTP.
¿Quieres saber qué más sobre IPFS y por qué no todas las herrmientas para construir la web3 están basadas en blockchain? Mira este articulo
¿Empezamos?
Una aclaración: Esta web está hecha desde cero y no es la primera implementacion de un framework para distribuir a IPFS, si quieres hacer un boilerplate más especifico deberias mirar un proyecto como VIPFS.
Setup
Nota: Se necesitan conociemientos basicos de: Shell y Node para este tutorial.
Para poder trabajar con Vite e IPFS necesitaremos instalar algunas cosas antes de meter las manos en la masa:
-
Asegurate de que tienes node 16 o superior instalado en tu sistema, sino descarga e instala node desde aca. Luego revisa si todo salio bién, escribiendo en la consola:
$ node -v
$ npm -v
Si todo está en orden deberian de salirte las versiones correspondientes:
-
Instala IPFS en tu sistema:
npm install -g ipfs
Esto instalara la CLI de IPFS con la cual trabajaremos mas adelante.
-
Creamos nuestro proyecto con Vite Y Vue3:
npm init vite@latest awesome-wallet --template vue
cd awesome-wallet
npm install
-
Vue Router, Vuex y Tailwind
npm i --save vue-router@next
npm i --save vuex@next
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Por favor si nunca has trabajado con tailwind mira como instalarlo para vite
Una vez instalado todo lo necesario para un proyecto "grande" con Vue, como normalmente lo haríamos, ahora estamos listos para empezar a escribir algo de código. Para esto necesitamos un editor o un IDE, puedes usar el que mas te guste, pero nosotros utilizaremos VSCode.
Pasos a seguir
A partir de ahora ponemos a tu disposición cada sección por el titulo de su implementación.
Estructura de proyecto y Usando el modo Dev
La estructura de las carpetas del proyecto deberian ser algo así (inicialmente):
.
├─ .gitignore
├─ index.html
├─ package-lock.json
├─ package.json
├─ README.md
├─ vite.config.js
├─ .vscode
│ └─ extensions.json
├─ public
│ └─ favicon.ico
└─ src
├─ App.vue
├─ main.js
├─ assets
│ └─ logo.png
└─ components
└─ HelloWorld.vue
Toma en cuenta que estará sujeta a cambios.
Podemos empezar el proyecto en modo dev con el sieguiente comando:
npm run dev
.
Una vez empecemos a trabajar, vite nos refrescara automáticamente las vistas para que podamos trabajar con un flujo mucho más cómodo.
Cada cosa que toquemos será mostrada en solo milisegundos en el navegador.
Vue setup y primeras vistas
Dentro de nuestro archivo ./src/main.js
necesitamos declarar un par de cosas
pero para ello primero vamos a crear algunos archivos para hacer el setup de
vue router y nuestras dos views:
Creamos una pequeña template para introducir el router:
./src/App.vue
<template>
<div class="w-full h-full flex flex-col">
<div class="flex px-20 py-10 items-center">
<h1 class="text-2xl">Awesome wallet</h1>
<div class="ml-auto flex items-center">
<router-link class="hover:text-indigo-500 font-bold px-4" to="/">Home</router-link>
<router-link class="hover:text-indigo-500 font-bold px-4" to="/auth">Join</router-link>
</div>
</div>
<div class="flex-1">
<router-view />
</div>
</div>
</template>
Configuramos nuestras rutas:
./src/router.js
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
component: () => import("./views/Home.vue"),
},
{
path: "/auth",
component: () => import("./views/Auth.vue"),
},
];
export default createRouter({
routes,
history: createWebHashHistory(),
});
Creamos el componente home:
./src/views/Home.vue
:
<template>
<div class="w-full h-full flex items-center justify-center">
<h1 class="text-3xl">Hello world</h1>
</div>
</template>
Creamos el componente Auth
./src/views/Auth.vue
:
<template>
<div class="w-full h-full flex items-center justify-center">
<h1 class="text-3xl">Auth</h1>
</div>
</template>
Por ultimo editamos nuestro ./src/main.js
:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import "./main.css";
const app = createApp(App);
app.use(router);
app.mount("#app");
Se ha modificado de como estaba anteriormente para incluir el router y el css proviniente de tailwind (ver como instalar tailwind).
Un poco de funcionalidad
Ahora podriamos decir que tenemos una app estática lista, pero para que nuestro sitio tenga alguna funcionalidad en concreto, agregaremos algo de código extra. A este punto podrías saltarte al paso de Build y Deploy.
El objetivo de nuestra app por ahora será encriptar un secret y poder verlo al escribir una contraseña que solo tu sepas, con el objetivo de en un próximo artículo, hacer una wallet con stellar-sdk.
Para esto, creamos los siguientes archivos:
./src/store.js
import { createStore } from "vuex";
export default createStore({
state: {
username: "",
encryptedToken: "",
},
mutations: {
setUsername(state, username) {
state.username = username;
},
setEncryptedToken(state, token) {
state.encryptedToken = token;
},
},
actions: {
setAuth({ commit }, { username, encryptedToken }) {
commit("setUsername", username);
commit("setEncryptedToken", encryptedToken);
},
removeAuth({ commit }) {
commit("setUsername", "");
commit("setEncryptedToken", "");
},
},
});
Modificamos nuestras vistas ./src/views/Home.vue
y
./src/views/Auth.vue
Y para terminar el front end creamos un servicio para generar, encriptar y
desencriptar nuestra key, en la el archivo
./src/services/crypto.service.js
import { AES, enc, lib } from "crypto-js";
export const randomUUID = () => {
return lib.WordArray.random(16).toString(enc.Hex);
};
export const encrypt = (text, key) => {
return AES.encrypt(text, key).toString();
};
export const decrypt = (text, key) => {
return AES.decrypt(text, key).toString(enc.Utf8);
};
Después de todo esto deberiamos tener algo así:
Ahora nuestra app está lista y en este punto podríamos subirla a cualquier proveedor de hosting para sitios estáticos, pero ahora empieza la magia de IPFS....
Build y Deploy
Ya solo nos queda construir nuestro sitio y distribuir a IPFS, para eso vamos a
añadir los siguientes scripts a nuestro package.json
{
"build": "vite build",
"deploy": "cd dist && npx all-relative && cd .. && jsipfs add -r dist/",
"release": "npm run build && npm run deploy"
}
Abre una terminal y ejecuta tu daemon:
$ jsipfs daemon
Abre otra terminal y ejecuta el script de deploy:
$ npm run release
Y finalmente tendremos algo como esto en la consola:
Esto significa que nuestros archivos están ahora en IPFS y disponibles a travez
de todas las gateways de IPFS, por lo que ¡ESTA VIVO!. Toma encuenta de que
a pesar de que todos los assets son accesibles con su hash el que nos interesa
es el de la carpeta base, por lo que lo copiamos para mas tarde:
QmbBkfkRbXZzLgvSx7FFnLmHHDEpZktAPVVA2Am8wtFkS4
.
Pero aca no podemos parar, ya que la accesibilidad de nuestra app sigue siendo limitada y debemos solucionarlo..
Pineando el contenido
Cuando creamos un archivo o varios de ellos en nuestro repositorio de IPFS,
solamente lo podemos ver nosotros en el localhost y nuestros amigos que tengan
un nodo propio solo si nuestro nodo está andando mientras ellos lo clonan. Por
lo que si miramos el hash de nuestra carpeta dist (por ejemplo)
QmbBkfkRbXZzLgvSx7FFnLmHHDEpZktAPVVA2Am8wtFkS4 en la gateway local
http://localhost:8080/ipfs/QmbBkfkRbXZzLgvSx7FFnLmHHDEpZktAPVVA2Am8wtFkS4
que
expone nuestro deamon (que seguimos ejecutando desde la build) vamos a poder
visusalizar nuestra app, pero si cerramos el daemon de ipfs y queremos pasarle
nuestro sitio a alguien, no lo va a poder ver, y esto no tiene sentido... ¿por
que tendria que dejar mi daemon activo para que otros puedan ver mi sitio?.
Pues por como IPFS funciona para que un archivo esté disponilbe para otros
usuarios tienen que haber nodos que estén "hosteando" nuestros archivos por
lo que si nuestros amigos (que ya clonaron la data) o yo cierro mi nodo y no
existe nadie disponible que haya clonado y esté ofreciendo nuestra data, si
alguien externo quisiese acceder con el hash a través de otra gateway, va a ser
imposible.
¿Entonces cual es la solución para que mi app no dependa de mi o de otros nodos relacionados a mi?
Hay dos respuestas validas para esto:
-
Solicitar nuestros archivos en gateways de nodos publicos que admitan el pineo de contenido. Aqui puedes encontrar una lista de nodos publicos en los que puedes solicitar el hash de tu carpeta dist, por ejemplo
https://ipfs.io/ipfs/QmbBkfkRbXZzLgvSx7FFnLmHHDEpZktAPVVA2Am8wtFkS4
la cual es la gateway publica oficial de ipfs (y el hash es nuestro), y no solo lo podrias hacer en uno o dos, mientras mas nodos tengan tu app, mejor. Pero esto tiene un par de problemas: 1) es publico por lo que puede llegar a ser lento en algunas ocaciones. 2) Es un trabajo que tienes que hacer manualmente, y se vuelve mas dificil si nombras tus archivos para que esten disponibles con ipns. -
Lo mas factible seria utilizar un servicio de pineo, lo que básicmanete es un servicio para replicar tu archivos de ipfs en una cantidad ridiculamente enorme de nodos en todo el mundo. Por lo que a de mas tambien funciona como una CDN y tu sitio es rapidamente accesible a traves de cualquier gateway a nivel mundial. En este caso existen servicios como pinata que con solo introducir el hash de tu app, tienes una replicación automatica en muchos nodos, al instante.
Como hayas decidido distribuir, ahora se puede decir que tu sitio está disponible para su uso a través de la red de la red IPFS.
Configurando un dominio
Este paso es totalmente opcional ya que ipfs siempre va a mantener el hash de tus archivos, y puedes versionar con ipfs name en IPNS.
Si tienes acceso a un servicio de nombres de dominio como Namecheap, Google Domains, GoDaddy o cualquier otro servicio de dominio incluyendo gratuitos como Freenom, puedes seguir estos pasos. Si no tienes un nombre de dominio para asignar, puedes leer esta sección. Nos sumergiremos en el uso de servicios de nombres descentralizados como Ethereum Naming Service (ENS) mas adelante.
-
Asegurate de copiar la url de tu sitio de una gateway confiable en este caso la gateway de infura hace algo especial, si colocas el hash como en cualquier gateway, te crea un subdminio base de la siguiente manera
https://infura-ipfs.io/ipfs/QmbBkfkRbXZzLgvSx7FFnLmHHDEpZktAPVVA2Am8wtFkS4
el resultado es un subdominio que puedes utilizar forwarded o aliased con tu proveedor de dominios preferido:https://bafybeif64byoox5dsv7whhzlhsvijxpvbs4oaeqwufpwgmad3mv2zgmtte.ipfs.infura-ipfs.io/
-
Configura la redirección permanente con tu proveedor de hosting, recuerda que el modo de forwarding sea de 301:
Esta solución redireccionara todas las request a la gateway que utilizas como tu gateway principal, y no actuará como un proxy. Esto puede que se convierta en una experiencia de usuario poco agradable y tal vez algo tosca, por lo que aun hay un layer mas que podrías implementar para que el resultado final sea lo mas pulido posible:
Utiliza un servicio de DNS dedicado: A de mas de ayudarte con tu sitio en un formato web2 proveyendo cosas como defensa contra ataques (que en este caso no importa demasiado) y otras mas utiles como manejo de los registros DNS en tu sitio y encriptado automatico por SSL, podemos considerar que tanto para Web 2 y 3 tener un proveedor de DNS por separado siempre es buena opción.
Para que tu dominio (en este caso vueipfs.ml) sea realmente el nombre de tu sitio en la barra de busquedas, sin redirecciones ni hacky stuff, debemos realizar un par de pasos con nuestro porvedor de DNS favorito (en este caso cloudflare).
- Apuntar los nameservers desde tu proveedor de dominios, a los namservers de
cloudflare
Para saber como agregar un sitio a tu proveedor de DNS debes mirar sus instrucciones.
- Generar un par de registros:
En un registro TXT a el subdominio _dnslink debemos colocar el siguiente
contenido: dnslink=/ipfs/{tu_hash_de_ipfs}
En un registro CNAME debemos apuntar nuestro root (@) a una gateway, en este
caso cloudflare tiene una propia: cloudflare-ipfs.com
Lo que hicimos es un implementación de dnslink si quieres saber como dnslink funciona mira esto.
Y ya tenemos un sitio con vida y la demo la puedes visitar en http://vueipfs.ml/
Conclusión
Las herramientas como IPFS son el paso hacia el futuro en cuanto a WEB3 se refiere y aunque el proceso de deploy pueda parecer algo más extenso, es algo que puedes hacer de extremo a extremo, empezando por tu pc sin depender de ningun servicio externo y aunque en este caso, utilizamos un servicio de pineo masivo, es posible que no necesites hacerlo si tu web adquiere cierta popularidad y a diferencia de los servicios centralizados no dependes de ninguna plataforma de pineo en exclusiva ni tienen control sobre tu contenido.
Me resulta emocionante saber que estamos en una época privilegiada del internet donde cualquier persona con los conocimientos técnicos sufiecientes tiene la oportunidad de construir proyectos increibles que están a la palma de la mano gracias al acercamiento cooperativo y horizontal de web3.
Con artículos como este, mediante ejemplos y proyectos claros que afiancen los conceptos básicos de web3, desde DevLand queremos enseñare que afuera existe un internet más independiente, más libre y autogobernado, que no solo gira en torno a Bitcoin, minería y trading, sino donde podemos desplegar sitios web a producción, aplicacones que hagan un uso justo y recompensado de los datos del usuario, sistemas de comunicación que sean realmente privados e incluso sistemas de gobernanza distribuidos.