Creando mi propio IDE con Angular y Node – Parte 2

¿Quieres aprender a crear tu propio IDE con Angular y Node?, chécate esta serie de tutoriales para llegar a crear uno propio de forma fácil y rápida.

Por si te lo perdiste, te dejo la parte anterior a este tutorial:

Hasta este punto ya tenemos:

  • Pestañas
  • Lista de archivos (o al menos la dependencia)
  • El Editor

¿Qué sigue entonces?, bueno, debido a que no podemos “avanzar” (en realidad sí podemos) si no tenemos un back que nos mande los archivos ya que nosotros a través de Angular directo no podemos acceder a la lista de directorios y demás, dejaremos eso al back. Quiero aclarar, que yo uso Windows, así que esto le toca adaptarlo a sus Linuxes o Macoses que usen si es el caso (aunque creo que el código es bastante genérico)…

Normalmente para este paso, usaríamos un WebSocket que de esa forma, podamos estar “guardando los datos por cada cambio posible”, o para leer los archivos que se creen desde atrás (es decir directo al directorio) pero para todo esto, mejor dejaríamos un botón, así que optaremos por hacer peticiones HTTP normales por ahora.

Así que lo primero es iniciar un nuevo proyecto con node, y una vez inicializado, vamos a instalar Express (como de costumbre porque es fácil y rápido)…

npm i express

Y creamos nuestro archivo index.js aquí no nos interesan buenas prácticas, ni nos interesa nada de principios y arquitecturas, así que haremos un cochinero 😉 porque la idea es aprender de forma básica…

Si no sabes cómo crear APIs básicas en Node + Express, te dejo el siguiente tutorial:

Para empezar vamos a definir una ruta “GET” en el root que nos mande directamente el listado de archivos en una ruta específica que en este caso es “la del usuario” (además claro que por seguridad) y quiero aclarar que vamos a usar código en español, a veces odio el código en español pero en este blog tratamos de darle mucha mayor prioridad a nuestro bello idioma, así que el código base se vería más o menos así:

const express = require("express");
const app = express();
const fs = require('fs');
const os = require("os");

const directorioUsuario = os.homedir();

app.get('/', (req, res) => {
    const directorioSeleccionado = req?.query?.directorio || '';
    return fs.readdir(directorioUsuario + '/' + directorioSeleccionado, {withFileTypes: true}, (error, archivos) => {
        if (error) {
            return res.send({
                exito: false,
            })
        }

        const formateado = [];
        for (const archivo of archivos) {
            formateado.push({
                nombre: archivo.name,
                esDirectorio: archivo.isDirectory()
            });
        }

        return res.send({
            exito: true,
            datos: formateado
        })
    });
});

app.listen(3000, () => {
    console.log("Servidor iniciado en el puerto 3000");
});

Como se observa, sobre esta misma ruta, podemos mandar a través del parámetro de consulta (query parameter) el directorio que hemos seleccionado, así, podamos hacer sub-peticiones con base al listado e ir agregando más cosas…

Por ejemplo, vamos a localhost:3000 y agregamos ?directorio=/Documents nos retorna el directorio de Documents.

Ahora, faltan 6 endpoints:

  • Crear archivos
  • Eliminar archivos
  • Crear directorios
  • Eliminar directorios
  • Actualizar código en los archivos
  • Leer código en los archivos

Sin embargo, vamos a combinar el crear archivos y el crear directorios en uno solo que solamente va a cambiar un parámetro, también el eliminar. Recordemos que esto es rápido, así que no vamos a validar datos ni nada de eso… OJO: No vamos a permitir el cambio de nombres (por ahora, quizá en un “Plus” de la guía lo hagamos)

Usaremos la base de la ruta (/ ) para la creación de archivos y directorios y como dije para eliminar, así que será POST = Crear, DELETE = Eliminar, mientras que dentro de la misma ruta con PUT actualizaremos el contenido y leeremos desde “/archivo” con GET y un parámetro. Vamos entonces al crear.

Primero ocupamos la librería de body-parser así que hay que instalarla:

npm i body-parser

Y después, vamos a necesitar integrarlo:


const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Y entonces, nuestro endpoint para crear el archivo quedaría de la siguiente manera:

app.post('/', (req, res) => {
    const esDirectorio = req?.body?.esDirectorio || false;
    const directorioSeleccionado = req?.query?.directorio || '';
    const nombreArchivoODirectorio = req?.body?.nombre || '';
    if (nombreArchivoODirectorio === '') {
        return res.send({
            exito: false,
        });
    }

    let directorioFinal = directorioUsuario + '/' + directorioSeleccionado + '/' + nombreArchivoODirectorio;
    if (directorioSeleccionado === '') {
        directorioFinal = directorioSeleccionado + '/' + nombreArchivoODirectorio;
    }

    if (esDirectorio) {
        return fs.mkdir(directorioFinal, (error) => {
            if (error) {
                return res.send({
                    exito: false,
                });
            }

            return res.send({
                exito: true,
            });
        })
    }

    return fs.writeFile(directorioFinal, '', (error) => {
        if (error) {
            return res.send({
                exito: false
            });
        }

        return res.send({
            exito: true
        });
    });
});

Vamos a medio explicar este endpoint, lo primero es saber, ¿es directorio o no?… en caso de que se cree en dónde a través de la ruta y por último, el nombre ya sea del archivo o del directorio (el cual técnicamente es un parámetro obligatorio), si es un directorio, pues lo creamos en la ruta que se indica o la ruta “raíz”, y si es un archivo, pues creamos el archivo que debe venir (o no) con extensión, ¿por qué “o no”? porque podríamos crear un archivo como log así tal cual y es válido, pero es un archivo.

Bastante fácil y sencillo, aquí NO vamos a agregarle contenido al archivo así que pues…lo dejamos puro, ahora vamos al eliminarlos. El endpoint es casi el mismo, excepto que es DELETE y que usaremos funciones diferentes.

app.delete('/', (req, res) => {
    const esDirectorio = req?.body?.esDirectorio || false;
    const directorioSeleccionado = req?.query?.directorio || '';
    const nombreArchivoODirectorio = req?.body?.nombre || '';
    if (nombreArchivoODirectorio === '') {
        return res.send({
            exito: false,
        });
    }

    let directorioFinal = directorioUsuario + '/' + directorioSeleccionado + '/' + nombreArchivoODirectorio;
    if (directorioSeleccionado === '') {
        directorioFinal = directorioSeleccionado + '/' + nombreArchivoODirectorio;
    }

    if (esDirectorio) {
        return fs.rmdir(directorioFinal, (error) => {
            if (error) {
                return res.send({
                    exito: false,
                });
            }

            return res.send({
                exito: true,
            });
        });
    }

    return fs.unlink(directorioFinal, (error) => {
        if (error) {
            return res.send({
                exito: false
            });
        }

        return res.send({
            exito: true
        });
    });
});

Aquí vamos a comentar algo… en ambos casos, deberíamos validar que el archivo existe (o directorio) y si es así, entonces sí lo elimine, si no existe, entonces, no debería eliminar nada pero… eso se los dejo de tarea a ustedes porque siempre hay oportunidades 😉 y como vieron solo cambió las funciones de fs.

Ya casi para terminar, vamos a crear el PUT para los archivos y así agregarles código.

app.put('/', (req, res) => {
    const directorioSeleccionado = req?.query?.directorio || '';
    const nombreArchivoODirectorio = req?.body?.nombre || '';
    const contenido = req?.body?.contenido || '';
    if (nombreArchivoODirectorio === '') {
        return res.send({
            exito: false,
        });
    }

    let directorioFinal = directorioUsuario + '/' + directorioSeleccionado + '/' + nombreArchivoODirectorio;
    if (directorioSeleccionado === '') {
        directorioFinal = directorioSeleccionado + '/' + nombreArchivoODirectorio;
    }

    return fs.writeFile(directorioFinal, contenido, (error) => {
        if (error) {
            return res.send({
                exito: false
            });
        }

        return res.send({
            exito: true
        });
    });
});

Fácil. ¿No creen?. Casi lo mismo que el de crear pero ahora sí asignamos contenido en el segundo parámetro de writeFile . Y ya saben, tienen la tarea de verificar si el archivo existe … o incluso de combinar estos ends o mejorar el código, por ahora, seguiremos así.

Ya para finalizar esta parte e irnos a la tercera, vamos a hacer el endpoint para leer los archivos.

app.get('/archivo', (req, res) => {
    const directorioSeleccionado = req?.query?.directorio || '';
    if (directorioSeleccionado === '') {
        return res.send({
            exito: false
        });
    }

    const directorioFinal = directorioUsuario + '/' + directorioSeleccionado;
    return fs.readFile(directorioFinal, 'utf8', (error, contenido) => {
        if (error) {

            return res.send({
                exito: false
            });
        }

        return res.send({
            exito: true,
            contenido
        });
    });
});

Fácil y sencillo, llamar cualquier archivo con la ruta (obvio requiere de que sea un archivo), ahora si no es un archivo va a tirar un error, por lo que, bueno, estas validaciones como dije, se las dejo a ustedes e incluso la seguridad, pero como esto correrá local y “temporal” pues venga, podemos avanzar así.

Y eso es todo por esta parte, te espero pronto en la tercera donde vamos a conectar el Front (o al menos lo inicial), le vamos a agregar más botones y así seguiremos en varios tutoriales hasta llegar a su estilización y cierre para que se vea 5/10 😉.

¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
0
+1
3
Total
0
Shares
Publicación anterior

Creando mi propio IDE con Angular y Node – Parte 1

Siguiente Publicación

PHP 8.2.0 ¡Ya disponible!

Publicaciones Relacionadas