Giter Club home page Giter Club logo

skate_park_app's Introduction

Solución Completa de la Prueba - Skate Park

Repositorio con el código solución de la prueba con la que se finaliza el módulo 8 Implementación de API backend Node Express de la beca dada por Talento Digital para Chile Desarrollo de aplicaciones Full Stack Javascript Trainee y dictada por Desafío Latam.

Tabla de Contenido

Deploy

El proyecto es 100% funcional y esta operativo en la web. Lo he desplegado en Render en el siguiente link

Requisitos

Requisitos 1 y 2 Requisitos 3 y 4 Requisito 5

Diagrama de Flujo

1.Página Home

Página Home

2.Página Registro

Página Registro

2.1.Registro de Participante Exitoso

Registro de Participante Exitoso

2.2.Verificación de Registro exitoso de Participante Exitoso Front End

Verificación de Registro exitoso de Participante Exitoso Front

2.3.Verificación de Registro exitoso de Participante Exitoso Back End

Verificación de Registro exitoso de Participante Exitoso Back

3.Página LogIn de Participante

Página LogIn de Participante

3.1.LogIn exitoso de Participante

LogIn exitoso de Participante

3.2.Página Cuenta de Usuario

Página Cuenta de Usuario

3.3.Token Expirado después de 2 minutos

Token Expirado después de 2 minutos

3.4.Alerta Edición exitosa

Alerta Edición exitosa

3.5.Verificación edición exitosa front end

Verificación edición exitosa front end

3.6.Verificación edición exitosa back end

Verificación edición exitosa back end

4.Página Admin

Página Admin

4.1.Alerta Cambio de estado exitoso de participante por Admin

Alerta Cambio de estado exitoso de participante por Admin

4.2.Verificación Cambio de estado exitoso de participante por Admin Front End

Verificación Cambio de estado exitoso de participante por Admin Front End

4.3.Verificación Cambio de estado exitoso de participante por Admin Back End

Verificación Cambio de estado exitoso de participante por Admin Back End

5.Alerta de Eliminación de Participante exitosa

Alerta de Eliminación de Participante exitosa

5.1.Verificación de Eliminación de participante Front End

Verificación de Eliminación de participante Front End

5.2.Verificación de Eliminación de participante Back End

Verificación de Eliminación de participante Back End

6.Alerta Archivo Muy Pesado

Alerta Archivo Muy Pesado

7.Alerta Tipo de Archivo No Permitido

Alerta Tipo de Archivo No Permitido

8.Alerta password y password repetida no coinciden

Alerta password y password repetida no coinciden

9.Página 404

Página 404

Soluciones

1. Crear una API REST con el Framework Express (3 Puntos)

Para manipular la creación, edición, lectura y eliminación de participantes he creado la siguiente API REST:

router.get("/participantes", getParticipantes);
router.post("/participante", postParticipante);
router.delete("/participante", deleteParticipante);
router.put("/participante", putEditParticipante);
router.patch("/participante", patchEditParticipante);

2. Servir contenido dinámico con express-handlebars (3 Puntos)

He servido contenido dinámico con express-handlebars por ejemplo la página de Admin para la cual he utilizado el siguiente código:

{{> header}}
<main class="py-3 main_page_admin">
  <h2>Administración</h2>
  <hr class="w-50" />
  <div class="table-responsive">
    <table class="table w-50 m-auto table-dark">
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">Foto</th>
          <th scope="col">Nombre</th>
          <th scope="col">Años de experiencia</th>
          <th scope="col">Especialidad</th>
          <th scope="col">Estado</th>
        </tr>
      </thead>
      <tbody>
        {{#if ( arrayVacio participantes) }}
        <tr>
          <td colspan="6">No hay participantes</td>
        </tr>
        {{else}}
        {{#each participantes}}
        <tr>
          <th scope="row">{{addOne @index}}</th>
          <td>
            <div class="participante_foto">
              <img
                src="/public/imagenes/{{ this.foto }}"
                alt="Imagen de {{ this.nombre }}"
              />
            </div>
          </td>
          <td>{{ this.nombre }}</td>
          <td>{{ this.anos_experiencia }}</td>
          <td>{{ this.especialidad }}</td>
          <td>
            {{#if this.estado }}
            <input
              data-email="{{ this.email }}"
              class="aprobado_checkbox"
              type="checkbox"
              checked
            />
            {{else}}
            <input
              data-email="{{ this.email }}"
              class="aprobado_checkbox"
              type="checkbox"
            />
            {{/if}}
          </td>
        </tr>
        {{/each}}
        {{/if}}
      </tbody>
    </table>
  </div>
</main>
{{> footer}}

<script type="module" src="/public/js/pages/admin.js"></script>

3. Ofrecer la funcionalidad Upload File con express-fileupload (2 Puntos)

Implemento la librería express-fileupload al cargar un archivo en la creación de un participante:

const maxSize = 1 * 1024 * 1024;
export default async function postParticipante(req, res) {
  try {
    const {
      email,
      nombre,
      anos_experiencia,
      especialidad,
      password,
      password2,
    } = req.body;
    const participantes = await getAllDataParticipantesQuery();
    if (participantes.find((participante) => participante.email === email)) {
      res.status(400).send("El email ya existe");
      return;
    }
    const {
      foto: { size, mimetype, mv: moveFile },
    } = req.files;
    if (password !== password2) {
      res.status(400).send("Las contraseñas no coinciden");
      return;
    }
    if (size > maxSize) {
      res.status(413).send("El tamaño del archivo es demasiado grande");
      return;
    }
    if (
      mimetype !== "image/jpeg" &&
      mimetype !== "image/png" &&
      mimetype !== "image/jpg" &&
      mimetype !== "image/gif" &&
      mimetype !== "image/webp"
    ) {
      res.status(415).send("El formato del archivo no es válido");
      return;
    }
    const nombreFoto = `${uuidv4().slice(0, 8)}.jpg`;
    moveFile(path.resolve("public", "imagenes", nombreFoto), async (err) => {
      if (err) {
        res.status(500).send(err);
        return;
      }
      try {
        const data = await postParticipantesQuery({
          email,
          nombre,
          password,
          anos_experiencia,
          especialidad,
          foto: nombreFoto,
          estado: false,
        });
        console.log(data);
        res.status(200).send("exito");
      } catch (error) {
        res.status(500).send(error);
      }
    });
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
}

4. Implementar seguridad y restricción de recursos o contenido con JWT (2 Puntos)

Implemento creación de token en el login de usuario tal y como muestro a continuación:

export default async function loginUser(req, res) {
  try {
    const { email, password } = req.body;
    if (!email || !password) {
      res.status(400).send("Faltan datos");
      return;
    }
    const participantes = await getAllDataParticipantesQuery();
    const participante = participantes.find(
      (participante) => participante.email === email
    );
    if (!participante) {
      res.status(400).send("El email no existe");
      return;
    }
    if (participante.password !== password) {
      res.status(400).send("La contraseña es incorrecta");
      return;
    }

    jwt.sign(participante, secretKey, { expiresIn: 2 * 60 }, (err, token) => {
      if (err) {
        res.status(500).send("Ha ocurrido un error");
        return;
      }
      res.status(200).json({ token });
    });
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
}

Implemento verificación de token al editar o eliminar data del participante. En este caso muestro la verificación de token al eliminar a participante:

export default async function deleteParticipante(req, res) {
  try {
    const token = req.headers.authorization.split(" ")[1];
    jwt.verify(token, process.env.SECRET_KEY_JWT, async (err, decoded) => {
      if (err) {
        if (err.message === "jwt expired") {
          res.status(401).send("El token ha expirado");
          return;
        }
        res.status(401).send("Token inválido");
        return;
      }
      const email = decoded.email;
      const data = await deleteParticipanteQuery(email);
      console.log(!data);
      if (!data) {
        res.status(404).send("Participante no encontrado");
        return;
      }
      console.log(path.resolve("public", "imagenes", data.foto));
      fs.unlink(path.resolve("public", "imagenes", data.foto), (err) => {
        if (err) {
          console.log(err);
          res.status(500).send("Error al borrar la imagen");
          return;
        }
        res.status(200).send("Participante eliminado");
      });
    });
  } catch (error) {
    console.log(error.message);
    res.status(500).send(error);
  }
}

Extra

1. Ruta para resetear la data en la base de datos y borrar las imagenes

He creado la siguiente ruta reset siguiente:

router.get("/reset", resetData);

La cual utiliza el siguiente middleware:

export default async function resetData(req, res) {
  try {
    const data = await resetDataQuery();
    if (data === "exito") {
      const files = await fs.readdir(path.resolve("public", "imagenes"));
      for (const file of files) {
        await fs.unlink(path.resolve("public", "imagenes", file));
      }
      res.status(200).send("Data Reseteada con exito !");
    }
  } catch (error) {
    res.status(500).send(error);
  }
}

La que a su vez utiliza la siguiente función resetDataQuery:

export default async function resetDataQuery() {
  try {
    const query = "DELETE FROM prueba_skate_park_skaters;";
    await pool.query(query);
    return "exito";
  } catch (error) {
    throw error;
  }
}

2. Script para resetear la data cada 30 minutos

setInterval(async () => {
  try {
    const data = await resetDataQuery();
    if (data === "exito") {
      const files = await fs.readdir(path.resolve("public", "imagenes"));
      for (const file of files) {
        await fs.unlink(path.resolve("public", "imagenes", file));
      }
      console.log("Data Reseteada con exito !");
    }
  } catch (error) {
    console.log("Error al resetear la data", error.message);
  }
}, 1800000);

skate_park_app's People

Contributors

waldohidalgo avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.