61 | );
62 | };
63 |
64 | export default ThemeSwitch;
65 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./pages/**/*.{js,ts,jsx,tsx}",
5 | "./components/**/*.{js,ts,jsx,tsx}",
6 | "./styles/**/*.{js,ts,jsx,tsx,scss,css}",
7 | ],
8 | theme: {
9 | screens: {
10 | sm: "500px",
11 | md: "800px",
12 | lg: "1000px",
13 | xl: "1250px",
14 | "2xl": "1450px",
15 | "3xl": "1900px",
16 | },
17 | fontFamily: {
18 | mono: ["Fira Code", "monospace"],
19 | space: ["Space Mono", "monospace"],
20 | raleway: ["Raleway", "sans-serif"],
21 | },
22 | extend: {
23 | borderWidth: {
24 | 1: "1px",
25 | 3: "3px",
26 | },
27 | borderRadius: {
28 | default: "15px",
29 | },
30 | colors: {
31 | accent: "#e61366",
32 | "accent-hover": "#be1559",
33 | "accent-active": "#a20f4e",
34 | doc: "#e2e2e2",
35 | "dark-doc": "#170c60",
36 | "doc-link": "#3c3cff",
37 | "dark-doc-link": "#e61366",
38 | "doc-font": "#282828",
39 | "dark-doc-font": "#e2e2e2",
40 | },
41 | boxShadow: {
42 | default: "0px 0px 15px 0px rgba(0, 0, 0, 0.3)",
43 | },
44 | dropShadow: {
45 | default: "0px 0px 10px rgba(0, 0, 0, 0.3)",
46 | notebook: "2px 2px 10px rgba(0, 0, 0, 0.8)",
47 | },
48 | backgroundImage: {
49 | main: "url('/assets/images/bg.svg')",
50 | github: "url('/assets/icons/GitHubLogo.png')",
51 | },
52 | backgroundSize: {
53 | 200: "200%",
54 | },
55 | keyframes: {
56 | shake: {
57 | "0%": {
58 | transform: "translateX(0)",
59 | },
60 | "50%": {
61 | transform: "translateX(10px)",
62 | },
63 | "100%": {
64 | transform: "translateX(0)",
65 | },
66 | },
67 | },
68 | animation: {
69 | shake: "shake 0.2s ease-in-out infinite",
70 | },
71 | },
72 | },
73 | plugins: [],
74 | };
75 |
--------------------------------------------------------------------------------
/public/assets/images/git.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/cheatsheets/node.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Node.js
3 | image: "/assets/images/node.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Inicializar un proyecto](#inicializar-un-proyecto)
10 | - [Instalar dependencias](#instalar-dependencias)
11 | - [Carpeta `node_modules`](#carpeta-node_modules)
12 | - [Instalar dependencias de desarrollo](#instalar-dependencias-de-desarrollo)
13 | - [Instalar dependencias de forma global](#instalar-dependencias-de-forma-global)
14 | - [Sección `scripts`](#sección-scripts)
15 |
16 |
17 |
18 | ## Inicializar un proyecto
19 |
20 | Para inicializar un projecto deberemos ejecutar el siguiente comando:
21 |
22 | ```bash
23 | npm init
24 | ```
25 |
26 | Este comando nos creará un archivo `package.json` con la información del proyecto, como el nombre, la versión, la descripción, etc.
27 |
28 | ## Instalar dependencias
29 |
30 | Para instalar dependencias deberemos ejecutar el siguiente comando:
31 |
32 | ```bash
33 | npm install
34 | ```
35 |
36 | Si corremos este comando sin el nombre de ninguna dependencia, nos instalará todas las dependencias que tengamos en el archivo `package.json` en la sección `dependencies`.
37 |
38 | ### Carpeta `node_modules`
39 |
40 | La carpeta `node_modules` es donde se instalan todas las dependencias de nuestro proyecto. Esta carpeta no se sube al repositorio, ya que es muy pesada y no es necesario que esté en el mismo. Para evitar esto, deberemos crear un archivo `.gitignore` en la raíz del proyecto y añadir la siguiente línea:
41 |
42 | ```bash
43 | node_modules/
44 | ```
45 |
46 | ## Instalar dependencias de desarrollo
47 |
48 | Para instalar dependencias de desarrollo deberemos ejecutar el siguiente comando:
49 |
50 | ```bash
51 | npm install --save-dev
52 | ```
53 |
54 | ## Instalar dependencias de forma global
55 |
56 | Para instalar dependencias de forma global deberemos ejecutar el siguiente comando:
57 |
58 | ```bash
59 | npm install -g
60 | ```
61 |
62 | ## Sección `scripts`
63 |
64 | Esta sección del archivo `package.json` es donde podemos añadir comandos que queramos ejecutar desde la terminal. Por ejemplo, si queremos utilizar `nodemon` para que se reinicie el servidor cada vez que guardemos un archivo, podemos añadir el siguiente comando:
65 |
66 | ```json
67 | {
68 | "name": "node",
69 | "version": "1.0.0",
70 | "description": "",
71 | "main": "index.js",
72 | "scripts": {
73 | "dev": "npx nodemon index.js"
74 | },
75 | "author": "",
76 | "license": "ISC"
77 | }
78 | ```
79 |
80 | Y luego ejecutarlo con el siguiente comando:
81 |
82 | ```bash
83 | npm run dev
84 | ```
85 |
--------------------------------------------------------------------------------
/public/assets/images/nextjs.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/download/socket.js:
--------------------------------------------------------------------------------
1 | class SoqueticError extends Error {
2 | constructor(message) {
3 | super(message);
4 | this.name = "SoqueticError";
5 | }
6 | }
7 |
8 | const socket = io("http://localhost:3000", {
9 | autoConnect: false,
10 | });
11 |
12 | socket.on("connect", () => {
13 | console.log("¡Conectado al backend!");
14 | });
15 |
16 | socket.on("connect_error", () => {
17 | throw new SoqueticError(
18 | "Error al conectar al backend. Revisá que el servidor no haya crasheado y esté corriendo en el puerto correcto.\nRecargá la página para reconectar."
19 | );
20 | });
21 |
22 | const assertConnection = (socket) => {
23 | if (!socket.active) {
24 | throw new SoqueticError(
25 | "No se puede enviar un evento si no hay conexión al backend.\nRecordá que tenés que llamar a connect2Server() para conectarte al backend."
26 | );
27 | }
28 | };
29 |
30 | const assertTypeIsString = (type) => {
31 | if (typeof type !== "string") {
32 | throw new SoqueticError(
33 | `El tipo de evento debe ser un string, pero es de tipo ${typeof type}`
34 | );
35 | }
36 | };
37 |
38 | const assertCallbackIsFunction = (callback) => {
39 | if (typeof callback !== "function") {
40 | throw new SoqueticError(
41 | `El callback debe ser una función, pero es de tipo ${typeof callback}`
42 | );
43 | }
44 | };
45 |
46 | const RESTCallbackDecorator = (callback) => {
47 | assertCallbackIsFunction(callback);
48 | return (response) => {
49 | if (response.status !== 200) {
50 | throw new SoqueticError(
51 | response.message ? response.message : "Error desconocido"
52 | );
53 | }
54 | callback(response.data);
55 | };
56 | };
57 |
58 | const send = (type, data, callback = () => {}) => {
59 | assertConnection(socket);
60 | assertTypeIsString(type);
61 | socket.emit("realTimeEvent", type, data, RESTCallbackDecorator(callback));
62 | };
63 |
64 | const receive = (type, callback) => {
65 | assertConnection(socket);
66 | assertTypeIsString(type);
67 | socket.on("realTimeEvent", (receivedType, data) => {
68 | if (receivedType === type) return callback(data);
69 | });
70 | };
71 |
72 | const fetchData = (type, callback) => {
73 | assertConnection(socket);
74 | assertTypeIsString(type);
75 | socket.emit("GETEvent", type, RESTCallbackDecorator(callback));
76 | };
77 |
78 | const postData = (type, data, callback = () => {}) => {
79 | assertConnection(socket);
80 | assertTypeIsString(type);
81 | socket.emit("POSTEvent", type, data, RESTCallbackDecorator(callback));
82 | };
83 |
84 | const connect2Server = (PORT = 3000) => {
85 | socket.io.uri = `http://localhost:${PORT}`;
86 | socket.connect();
87 | };
88 |
--------------------------------------------------------------------------------
/cheatsheets/mysql2.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mysql2
3 | image: "/assets/images/mysql2.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Introducción](#introducción)
10 | - [Instalación](#instalación)
11 | - [Conexión](#conexión)
12 | - [Consultas (Queries)](#consultas-queries)
13 | - [Consultas sin parámetros](#consultas-sin-parámetros)
14 | - [Consultas con parámetros](#consultas-con-parámetros)
15 |
16 |
17 |
18 | ## Introducción
19 |
20 | `mysql2` es una librería de Node.js que permite realizar una conexión a una base de datos. En este documento utilizaremos en específico la parte de la librería que utiliza promesas para manejar la asincronía (`mysql2/promise`).
21 |
22 | ## Instalación
23 |
24 | Para instalar `mysql2` deberemos ejecutar el siguiente comando (dentro de un proyecto de Node.js):
25 |
26 | ```bash
27 | npm install mysql2
28 | ```
29 |
30 | ## Conexión
31 |
32 | Para conectarnos a la base de datos deberemos añadir el siguiente código:
33 |
34 | ```js
35 | import mysql from "mysql2/promise";
36 |
37 | const connection = await mysql.createConnection({
38 | host: "",
39 | user: "",
40 | password: "",
41 | database: "",
42 | });
43 | ```
44 |
45 | En `connection` tendremos un objeto que nos permitirá hacer consultas a la base de datos.
46 |
47 | **Aclaración:** (recordar que para poder importar las librerías con `import` y para poder utilizar `await` fuera de una función deben utilizar `"type": "module"` en el `package.json`)
48 |
49 | ## Consultas (Queries)
50 |
51 | ### Consultas sin parámetros
52 |
53 | Para hacer consultas a la base de datos deberemos añadir el siguiente código:
54 |
55 | ```js
56 | const [result, fields] = await connection.query("ACÁ VA LA CONSULTA SQL");
57 | ```
58 |
59 | En el caso de que la consulta sea un `SELECT`, en `result` tendremos un array con los resultados de la consulta. En caso de que sea un `INSERT`, `UPDATE` o `DELETE`, en `result` tendremos un objeto con información sobre la consulta.
60 |
61 | En `fields` tendremos información sobre las columnas de la tabla, como el nombre, el tipo, etc.
62 |
63 | ### Consultas con parámetros
64 |
65 | Para hacer consultas a la base de datos con parámetros deberemos añadir el siguiente código:
66 |
67 | ```js
68 | const [result, fields] = await connection.query(
69 | "ACÁ VA LA CONSULTA SQL CON ? Y ?",
70 | [parametro1, parametro2]
71 | );
72 | ```
73 |
74 | Este código funciona de la misma manera que el anterior, pero en este caso, los `?` serán reemplazados por los parámetros que pasemos en el array. Es decir, la query final que se ejecutará será `"ACÁ VA LA CONSULTA SQL CON parametro1 Y parametro2"`.
75 |
76 | Esto es útil para evitar inyecciones SQL, haciendo mucho más seguro nuestro código.
77 |
78 | **Aclaración:** Los parámetros deben ser pasados en el mismo orden que los `?` en la consulta SQL. Porque el primer `?` será reemplazado por el primer elemento del array, el segundo `?` por el segundo elemento, y así sucesivamente.
79 |
--------------------------------------------------------------------------------
/components/utils/Terminal/Autocomplete.tsx:
--------------------------------------------------------------------------------
1 | import { useContext, useEffect, useReducer, useState } from "react";
2 | import { TerminalContext } from "../../../contexts/TerminalContext";
3 | import { twMerge } from "tailwind-merge";
4 |
5 | export type AutocompleteType = {
6 | hidden: string;
7 | visible: string;
8 | };
9 |
10 | type AutocompleteComponentType = {
11 | content: string;
12 | list: string[];
13 | padding?: string;
14 | };
15 |
16 | const autocompleteReducer = (
17 | state: AutocompleteType,
18 | action: {
19 | type: "SET" | "CLEAR";
20 | payload: { content: string; length: number };
21 | }
22 | ) => {
23 | switch (action.type) {
24 | case "SET":
25 | const { content, length } = action.payload;
26 | const hidden = content.slice(0, length);
27 | const visible = content.slice(length);
28 | return { hidden, visible };
29 | case "CLEAR":
30 | return { hidden: "", visible: "" };
31 | default:
32 | return state;
33 | }
34 | };
35 |
36 | export const AutocompleteCommand = ({
37 | content,
38 | list,
39 | padding,
40 | }: AutocompleteComponentType) => {
41 | const [autocompleteValue, setAutocompleteValue] = useReducer(
42 | autocompleteReducer,
43 | {
44 | hidden: "",
45 | visible: "",
46 | }
47 | );
48 |
49 | const { error } = useContext(TerminalContext);
50 |
51 | useEffect(() => {
52 | if (content === "")
53 | return setAutocompleteValue({
54 | type: "CLEAR",
55 | payload: { content: "", length: 0 },
56 | });
57 |
58 | const command = list.find((c) => c.startsWith(content));
59 |
60 | if (command) {
61 | setAutocompleteValue({
62 | type: "SET",
63 | payload: { content: command, length: content.length },
64 | });
65 | } else {
66 | setAutocompleteValue({
67 | type: "CLEAR",
68 | payload: { content: "", length: 0 },
69 | });
70 | }
71 | }, [content]);
72 |
73 | return (
74 | <>
75 |
76 | {padding}
77 | {autocompleteValue.hidden}
78 |
79 |
82 | {autocompleteValue.visible}
83 |
84 | >
85 | );
86 | };
87 |
88 | export const AutocompleteFile = ({
89 | list,
90 | content,
91 | }: {
92 | list: string[];
93 | content: string;
94 | }) => {
95 | const [fileName, setFileName] = useState("");
96 | const [padding, setPadding] = useState("");
97 |
98 | useEffect(() => {
99 | const command = content.split(" ");
100 | // console.log(file);
101 | setFileName(command[1] || "");
102 | setPadding(command[0] + " ");
103 | }, [content]);
104 |
105 | return (
106 |
107 | );
108 | };
109 |
--------------------------------------------------------------------------------
/public/assets/images/bash.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/cheatsheets/bash.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Terminal (bash)
3 | image: "/assets/images/bash.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [¿Qué terminal usar?](#qué-terminal-usar)
10 | - [Sistema de archivos](#sistema-de-archivos)
11 | - [Rutas absolutas](#rutas-absolutas)
12 | - [Rutas relativas](#rutas-relativas)
13 | - [Comandos básicos](#comandos-básicos)
14 | - [Comandos de navegación en directorios](#comandos-de-navegación-en-directorios)
15 | - [Comandos de programas](#comandos-de-programas)
16 |
17 |
18 |
19 | ## ¿Qué terminal usar?
20 |
21 | Para utilizar los comandos que aparecerán en este documento, no será posible utilizar la terminal de Windows (cmd). Para ello, se recomienda utilizar la terminal Windows PowerShell o la terminal de Linux (Ubuntu).
22 |
23 | ## Sistema de archivos
24 |
25 | ### Rutas absolutas
26 |
27 | Las rutas absolutas son una forma de ubicar un archivo en una computadora/servidor de manera global, es decir, desde cualquier ubicación arbitraria, es posible acceder al archivo especificado por medio de estas. Las utilizamos todos los días por ejemplo en el explorador de archivos, todos tenemos los archivos de los distintos programas en ‘C:\Program Files’ la cual es una ruta absoluta ya que empieza con el disco (`C:\`).
28 |
29 | ### Rutas relativas
30 |
31 | Las rutas relativas, en cambio, son una forma de localizar un archivo desde la ubicación actual en la que se encuentra el usuario. Podemos pensarlo como las direcciones que le damos a alguien que ya está ubicado en una ruta, para llegar al lugar que queremos. Para esta tarea, utilizaremos los siguientes comandos:
32 |
33 | **‘./’**: Este comando representa la carpeta actual del archivo en cuestión.
34 | **‘../’**: Este comando representa la carpeta padre de la carpeta actual.
35 |
36 | **Ejemplo:**
37 | Si el árbol de carpetas fuera el siguiente:
38 |
39 | Si nosotros estamos en el archivo `index.php`, entonces para acceder a la carpeta test debemos utilizar la ruta relativa `../test/`, la cual significa subir una carpeta en el árbol y luego entrar a la carpeta `test`.
40 |
41 | ## Comandos básicos
42 |
43 | ### Comandos de navegación en directorios
44 |
45 | | Comando | Uso | Descripción |
46 | | ------- | --------------------- | ----------------------------- |
47 | | `cd` | `cd carpeta_proyecto` | Cambia de directorio |
48 | | `cd` | `cd ..` | Vuelve al directorio anterior |
49 | | `ls` | `ls` | Lista los archivos |
50 | | `pwd` | `pwd` | Muestra la ruta actual |
51 |
52 | ### Comandos de programas
53 |
54 | | Comando | Uso | Descripción |
55 | | ---------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
56 | | `explorer` | `explorer .` | Abre el explorador de archivos en la carpeta actual |
57 | | `code` | `code .` | Abre Visual Studio Code en la carpeta actual |
58 | | `git` | `git status` | Muestra el estado del repositorio ([para más comandos](https://cheatsheets-nachovigilante.vercel.app/cheatsheet/git)) |
59 | | `npm` | `npm start` | Ejecuta el comando `start` de `package.json` ([para más comandos](https://cheatsheets-nachovigilante.vercel.app/cheatsheet/node)) |
60 |
--------------------------------------------------------------------------------
/public/assets/images/cloudinary.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cheatsheets de TIC
2 |
3 | Este es el repositorio de la web de cheatsheets de TIC. Para acceder a la web [hacer click acá](https://cheatsheets-nv.vercel.app/).
4 |
5 | ## ¿Cómo hago un cambio?
6 |
7 | ### ¿Mi cambio tiene que ser fundamental?
8 |
9 | No. Todos los cambios son importantes, incluso si el cambio únicamente incluye agregar una tilde en este mismo documento. Cada uno puede aportar su parte para mejorar la calidad de este (y otros...) repositorios. De eso se trata la filosofía [Open Source](https://opensource.org/about), no es necesario saber programar mejor que otro, o entender más de un lenguaje u otra herramienta, todos cometemos errores y cualquiera puede ser capaz de corregirlos. Incluso si el cambio no es significativo, es una buena forma de practicar el proceso que conlleva realizar una [pull request](https://docs.github.com/es/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) (PR).
10 |
11 | ### Pasos a seguir
12 |
13 | Para proponer algún cambio que agregue información o mejore algo en el repo hay que seguir los siguientes pasos:
14 |
15 | - Hacer un fork al repo
16 | - Realizar algún cambio detallando en el/los commits (utilizando [conventional commits](https://www.conventionalcommits.org/)) lo realizado
17 | - [Hacer un PR](#ejemplo-de-pr) con el/los commits realizados
18 | - Esperar la respuesta al PR
19 |
20 | ### Convención para el commit message
21 |
22 | Para los commits, vamos a utilizar una convención llamada [conventional commits](https://www.conventionalcommits.org/). El proyecto está configurado para que no se pueda committear si no se sigue esta convención.
23 |
24 | **Importante si usas linux o mac:** correr `npm run unix` una vez o no van a poder commitear.
25 |
26 | ### Ejemplo de PR
27 |
28 | En esta sección podemos ver un [ejemplo de un PR](https://github.com/nachovigilante/cheatsheets/pull/1) que realizó [@Sponja](https://github.com/Sponja-) para arreglar un problema en el índice del cheatsheet de PHP.
29 |
30 | 
31 |
32 | El PR puede recibir una respuesta positiva o negativa, si la respuesta es positiva el cambio se acepta y si es negativa se rechaza, lo que no significa que la corrección o el arreglo no es necesario, sino que tal vez es necesario mejorar la corrección antes de aceptarla.
33 |
34 | Una vez que el PR es aceptado, el cambio se aplica al repo y se generará un nuevo commit con el cambio, como el que podemos [ver en este commit](https://github.com/nachovigilante/cheatsheets/commit/1fc56153a09720a09a724b600c7386423c83cd66).
35 |
36 | ## Agregar nuevos cheatsheets
37 |
38 | Si se quiere agregar un nuevo cheatsheet de algún lenguaje o herramienta que no se encuentra en el repo, ésta debe ser agregada en formato de archivo `.md`, en la carpeta `/cheatsheets/` con el nombre del lenguaje o la herramienta en cuestión. Es importante, además de seguir los pasos en la [sección anterior](#cómo-hago-un-cambio), cumplir los requerimientos obligatorios.
39 |
40 | ### Requisitos
41 |
42 | - Agregar además del `.md` un logo correspondiente en la carpeta `/assets/images/` que debe estar en formato `svg` y debe tener el mismo nombre que el archivo `.md` (ejemplo: `php.md` y `php.svg`). Intentar que el logo tenga un tamaño adecuado de alrededor de 20x20px, para que se vea bien en la web.
43 |
44 | - Tener una sección de metadata que tenga la siguiente estructura:
45 |
46 | ```plaintext
47 | ---
48 | title: {título}
49 | ---
50 | ```
51 |
52 | - La primer sección debe ser llamada "Índice" y debe contener una tabla de contenidos (`TOC`), con la misma estructura que la presente en los cheatsheets que se encuentran actualmente en la carpeta `/cheatsheets/`, seguida de un ` ` por razones puramente estéticas
53 |
54 | ### Recomendaciones
55 |
56 | - Citar las fuentes de información para acelerar el proceso de verificación y validación
57 | - Hacer uso de las herramientas que brinda markdown para mejorar la legibilidad de la información, aligerando también el proceso de feedback
58 |
--------------------------------------------------------------------------------
/public/assets/icons/Sun.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/images/react.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/pages/cheatsheet/[slug].tsx:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import matter from "gray-matter";
3 | import path from "path";
4 | import Head from "next/head";
5 | import "highlight.js/styles/github-dark.css";
6 | import React, { useContext, useEffect } from "react";
7 | import FloatingButton from "../../components/utils/FloatingButton";
8 | import { LoadingContext } from "../../contexts/LoadingContext";
9 | import { ThemeContext } from "../../contexts/ThemeContext";
10 | import router from "next/router";
11 | import hljs from "highlight.js";
12 | import marked from "marked-katex";
13 | import katex from "katex";
14 | import { twMerge } from "tailwind-merge";
15 | import prisma from "../../highlighters/prisma";
16 |
17 | hljs.registerLanguage("prisma", prisma);
18 |
19 | marked.setOptions({
20 | highlight: function (code, lang) {
21 | if (typeof lang === "undefined") {
22 | return hljs.highlightAuto(code).value;
23 | } else if (lang === "nohighlight") {
24 | return code;
25 | } else {
26 | return hljs.highlight(lang, code).value;
27 | }
28 | },
29 | kaTex: katex,
30 | });
31 |
32 | type CheatsheetPageProps = {
33 | frontmatter: {
34 | title: string;
35 | image: string;
36 | };
37 | content: string;
38 | slug: string;
39 | };
40 |
41 | const CheatsheetPage = ({
42 | frontmatter: { title, image },
43 | content,
44 | slug,
45 | }: CheatsheetPageProps) => {
46 | const { setLoading } = useContext(LoadingContext);
47 | const { theme } = useContext(ThemeContext);
48 |
49 | useEffect(() => {
50 | router.events.on("routeChangeStart", () => setLoading(true));
51 | router.events.on("routeChangeComplete", () => setLoading(false));
52 |
53 | return () => {
54 | router.events.off("routeChangeStart", () => setLoading(true));
55 | router.events.off("routeChangeComplete", () => setLoading(false));
56 | };
57 | }, []);
58 |
59 | // console.log(slug);
60 |
61 | return (
62 | <>
63 |
64 | {title}
65 |
66 |
72 |
73 | {title}
74 |
75 |
84 |
85 |
86 | window.scrollTo(0, 0)}
88 | ariaLabel="Scroll to the top"
89 | >
90 |
91 |
92 |
107 | );
108 | };
109 |
110 | const Terminal = ({ cheatsheets }: TerminalProps) => {
111 | return (
112 |
113 |
114 |
115 | );
116 | };
117 |
118 | export default Terminal;
119 |
--------------------------------------------------------------------------------
/cheatsheets/git.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Git-GitHub
3 | image: "/assets/images/git.svg"
4 | ---
5 |
6 |
7 |
8 | ## Índice
9 |
10 | - [Índice](#índice)
11 | - [¿Qué es?](#qué-es)
12 | - [Comandos básicos](#comandos-básicos)
13 | - [Crear un nuevo repositorio](#crear-un-nuevo-repositorio)
14 | - [Clonar un repositorio](#clonar-un-repositorio)
15 | - [Añadir un archivo](#añadir-un-archivo)
16 | - [Añadir todos los archivos](#añadir-todos-los-archivos)
17 | - [Hacer un commit](#hacer-un-commit)
18 | - [Hacer un push](#hacer-un-push)
19 | - [Hacer un pull](#hacer-un-pull)
20 | - [Ver el estado del repositorio](#ver-el-estado-del-repositorio)
21 | - [Ver el historial de commits](#ver-el-historial-de-commits)
22 | - [Branches](#branches)
23 | - [Nueva branch](#nueva-branch)
24 | - [Ver branches existentes](#ver-branches-existentes)
25 | - [Moverse entre branches](#moverse-entre-branches)
26 | - [Moverse a una branch remota](#moverse-a-una-branch-remota)
27 | - [Mergear branches](#mergear-branches)
28 | - [Eliminar una branch](#eliminar-una-branch)
29 | - [GitHub](#github)
30 | - [Crear un nuevo repositorio en GitHub](#crear-un-nuevo-repositorio-en-github)
31 | - [Clonar un repositorio de GitHub](#clonar-un-repositorio-de-github)
32 | - [Añadir un colaborador](#añadir-un-colaborador)
33 |
34 |
35 | ## ¿Qué es?
36 |
37 | Git es un sistema de control de versiones distribuido, es decir, que cada usuario tiene una copia completa del repositorio. Esto nos permite trabajar de forma local y sincronizar los cambios con el repositorio remoto cuando queramos.
38 |
39 | ## Comandos básicos
40 |
41 | ### Crear un nuevo repositorio
42 |
43 | ```bash
44 | git init
45 | ```
46 |
47 | ### Clonar un repositorio
48 |
49 | ```bash
50 | git clone https://www.github.com/username/repo.git
51 | ```
52 |
53 | ### Añadir un archivo
54 |
55 | ```bash
56 | git add file.txt
57 | ```
58 |
59 | ### Añadir todos los archivos
60 |
61 | ```bash
62 | git add .
63 | ```
64 |
65 | ### Hacer un commit
66 |
67 | ```bash
68 | git commit -m "Mensaje del commit"
69 | ```
70 |
71 | Descripción completa
72 |
73 | ```bash
74 | git commit -m "Mensaje del commit" -m "Descripción del commit"
75 | ```
76 |
77 | ### Hacer un push
78 |
79 | ```bash
80 | git push origin main
81 | ```
82 |
83 | Puede también ser en repositorios más viejos que sea `git push origin master`. Master o Main son los nombres de la branch que están pusheando, y puede variar si están pusheando otra branch.
84 |
85 | ### Hacer un pull
86 |
87 | ```bash
88 | git pull origin main
89 | ```
90 |
91 | Al igual que en push, en repositiorios más viejos puede ser `git pull origin master`
92 |
93 | ### Ver el estado del repositorio
94 |
95 | ```bash
96 | git status
97 | ```
98 |
99 | ### Ver el historial de commits
100 |
101 | ```bash
102 | git log
103 | ```
104 |
105 | ## Branches
106 |
107 | ### Nueva branch
108 |
109 | ```bash
110 | git switch -c
111 | ```
112 |
113 | La opción -c es para crear la branch, de otra forma `switch` solo nos deja movernos entre branches existentes. Si se quiere crear una branch sin movernos, es `git branch `.
114 |
115 | ### Ver branches existentes
116 |
117 | ```bash
118 | git branch
119 | ```
120 |
121 | ### Moverse entre branches
122 |
123 | ```bash
124 | git switch
125 | ```
126 |
127 | ### Moverse a una branch remota
128 |
129 | ```bash
130 | git fetch
131 | git switch
132 | ```
133 |
134 | Se hace `git fetch` antes del `switch` para que el repositorio local conozca que existe esa branch en el repositorio remoto.
135 |
136 | ### Mergear branches
137 |
138 | ```bash
139 | git merge
140 | ```
141 |
142 | Se mergea sobre la branch actual la branch a mergear. **NO** elimina la branch a mergear.
143 |
144 | ### Eliminar una branch
145 |
146 | ```bash
147 | git branch -D
148 | ```
149 |
150 | La opción `-D` es para eliminar la branch, de otra forma `branch` solo crea esa branch, que falla si la branch ya existía.
151 |
152 | ## GitHub
153 |
154 | GitHub es una plataforma de desarrollo colaborativo que permite alojar proyectos utilizando el sistema de control de versiones Git.
155 |
156 | ### Crear un nuevo repositorio en GitHub
157 |
158 | Para crear un nuevo repositorio deberemos ir a la página de [GitHub](https://github.com/) y hacer click en el botón `New repository`.
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 | ### Clonar un repositorio de GitHub
167 |
168 | Para clonar un repositorio deberemos ir a la página del repositorio y hacer click en el botón `Clone or download`.
169 |
170 | ### Añadir un colaborador
171 |
172 | Para añadir un colaborador deberemos ir a la página del repositorio y hacer click en `Settings`. Una vez dentro, deberemos ir a la sección `Collaborators` y añadir el nombre de usuario del colaborador.
173 |
--------------------------------------------------------------------------------
/public/assets/images/mysql2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/public/assets/images/ino.svg:
--------------------------------------------------------------------------------
1 |
2 |
46 |
--------------------------------------------------------------------------------
/cheatsheets/postgres.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: postgres
3 | image: "/assets/images/mysql2.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Introducción](#introducción)
10 | - [Creación de una base de datos en Vercel](#creación-de-una-base-de-datos-en-vercel)
11 | - [Conexión a la base de datos](#conexión-a-la-base-de-datos)
12 | - [pgAdmin](#pgadmin)
13 | - [Conexión a la base de datos desde pgAdmin](#conexión-a-la-base-de-datos-desde-pgadmin)
14 | - [Creación de una tabla](#creación-de-una-tabla)
15 | - [Tipos de datos](#tipos-de-datos)
16 | - [Autoincremental](#autoincremental)
17 | - [Claves foráneas](#claves-foráneas)
18 | - [pg en Node.js](#pg-en-nodejs)
19 | - [Instalación](#instalación)
20 | - [Utilización](#utilización)
21 | - [Conexión](#conexión)
22 | - [Consultas](#consultas)
23 |
24 |
25 |
26 | ## Introducción
27 |
28 | PostgreSQL es un motor de base de datos relacional SQL. Será el encargado de almacenar y gestionar los datos de nuestra aplicación. PostgreSQL es el sistema más común entre los sistemas que prestan servicio de hosting de bases de datos de manera gratuita en algunos planes. En particular, esta cheatsheet se enfoca en utilizar PostgreSQL, pgAdmin (una interfaz gráfica para PostgreSQL) y conectarlo a una base de datos hosteada en Vercel.
29 |
30 | ## Creación de una base de datos en Vercel
31 |
32 | Una vez que ingresaron a su cuenta de Vercel, y crearon un proyecto en el que desean utilizar una base de datos PostgreSQL, sigan los siguientes pasos para crear una base de datos en Vercel:
33 |
34 | Ir a la sección "Storage" del proyecto.
35 |
36 |
37 | 
38 |
39 |
40 | Luego, tendrán que seleccionar para crear una nueva base de datos en PostgreSQL.
41 |
42 | ## Conexión a la base de datos
43 |
44 | Para conectarse a la base de datos, tanto desde pgAdmin como desde la aplicación, se necesitan datos que se encontrarán en la sección "Quickstart" de la base de datos creada en Vercel.
45 |
46 |
47 | 
48 |
49 |
50 | ## pgAdmin
51 |
52 | pgAdmin es una interfaz gráfica para PostgreSQL que permite visualizar y manipular los datos de la base de datos.
53 |
54 | ### Conexión a la base de datos desde pgAdmin
55 |
56 | Para conectarse a la base de datos desde pgAdmin, sigan los siguientes pasos:
57 |
58 | 1. Descargar pgAdmin desde [pgAdmin](https://www.pgadmin.org/download/).
59 | 2. Abrir pgAdmin y agregar un nuevo servidor
60 | 
61 | 3. Completar los datos del servidor con los datos de la base de datos de Vercel vistos en la sección "Conexión a la base de datos".
62 | 
63 |
64 | Una vez que se conectaron a la base de datos, podrán visualizar y manipular los datos de las distintas tablas que tengan en la base de datos.
65 |
66 | Es importante remarcar que si la base de datos que utilizaremos es la llamada "verceldb", pues no tenemos permisos para editar la llamada "postgres".
67 |
68 | ### Creación de una tabla
69 |
70 | Para crear una tabla en la base de datos, deberán acceder desde el menú de la izquierda a "Servers" -> "nombre_del_servidor" -> "Databases" -> "verceldb" -> "Schemas" -> "public" -> "Tables" -> "Create" -> "Table...".
71 |
72 | Luego, deberán completar los datos de la tabla que desean crear. Y agregando las columnas que deseen.
73 |
74 | 
75 |
76 | En general, queremos que todos los campos sean NOT NULL, excepto los que no sean necesarios.
77 |
78 | #### Tipos de datos
79 |
80 | - `integer`: Números enteros.
81 | - `real`: Números con coma.
82 | - `character varying`: Es el equivalente a `varchar` en otros motores de base de datos.
83 | - `text`: Texto.
84 | - `date`: Fecha.
85 | - `timestamp with time zone`: Fecha y hora.
86 | - `boolean`: Verdadero o falso.
87 |
88 | ##### Autoincremental
89 |
90 | Para que un campo sea autoincremental, deberán seleccionar la columna y darle el tipo de dato `serial`. Es importante que el campo que sea autoincremental sea la clave primaria de la tabla.
91 |
92 | ### Claves foráneas
93 |
94 | Para agregar una clave foránea, deberán dar botón derecho en la tabla, entrar en la opción "Properties". Luego, en la pesataña "Constraints" seleccionar seleccionar "Foreign Key". Finalmente, deberán completar los datos de la clave foránea (la columna que será foreign key, la clave que referencia y su respectiva tabla).
95 |
96 | ## pg en Node.js
97 |
98 | Para conectarse a la base de datos desde Node.js, se puede utilizar el paquete `pg`. Aquí su [documentación](https://node-postgres.com/).
99 |
100 | ### Instalación
101 |
102 | Para instalarlo, deberán correr el siguiente comando (dentro de un proyecto de Node.js):
103 |
104 | ```bash
105 | npm install pg
106 | ```
107 |
108 | ### Utilización
109 |
110 | #### Conexión
111 |
112 | Una vez que instalaron el paquete, podrán conectarse a la base de datos y realizar consultas.
113 |
114 | ```javascript
115 | import { Client } from "pg"; // Importamos el cliente de pg (recordar que para utilizar 'import' es necesario usar "type": "module" en el package.json)
116 |
117 | // Pueden (y deberían) utilizar variables de entorno para almacenar los datos de conexión (dotenv)
118 | const client = new Client({
119 | user: "",
120 | host: "",
121 | database: "",
122 | password: "",
123 | port: 5432,
124 | ssl: true
125 | });
126 |
127 | client.connect(); // Nos conectamos a la base de datos
128 | ```
129 |
130 | #### Consultas
131 |
132 | Una vez que se conectaron a la base de datos, podrán realizar consultas. Por ejemplo, para obtener todos los registros de una tabla:
133 |
134 | ```javascript
135 | const res = await client.query("SELECT * FROM ");
136 | console.log(res.rows); // Imprimimos los registros
137 | ```
138 |
139 | Para insertar un registro en una tabla:
140 |
141 | ```javascript
142 | await client.query("INSERT INTO (columna1, columna2) VALUES ($1, $2)", [valor1, valor2]);
143 | ```
144 |
145 | Notar que en el segundo argumento de `query` se pasan los valores que se quieren insertar en la tabla. Esto se escribe un poco distinto a los prepared statements de MySQL (utilizábamos el `?`). En este caso, se utilizan los `$1`, `$2`, etc. para referenciar a los valores que se quieren insertar, nos permite cambiar el orden de los valores sin tener que cambiar el orden de los `$`.
146 |
--------------------------------------------------------------------------------
/public/assets/fontawesome/scss/_animated.scss:
--------------------------------------------------------------------------------
1 | // animating icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-beat {
5 | animation-name: #{$fa-css-prefix}-beat;
6 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
7 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
8 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
9 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
10 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out);
11 | }
12 |
13 | .#{$fa-css-prefix}-bounce {
14 | animation-name: #{$fa-css-prefix}-bounce;
15 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
16 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
17 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
18 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
19 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1));
20 | }
21 |
22 | .#{$fa-css-prefix}-fade {
23 | animation-name: #{$fa-css-prefix}-fade;
24 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
25 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
26 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
27 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
28 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1));
29 | }
30 |
31 | .#{$fa-css-prefix}-beat-fade {
32 | animation-name: #{$fa-css-prefix}-beat-fade;
33 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
34 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
35 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
36 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
37 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1));
38 | }
39 |
40 | .#{$fa-css-prefix}-flip {
41 | animation-name: #{$fa-css-prefix}-flip;
42 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
43 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
44 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
45 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
46 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out);
47 | }
48 |
49 | .#{$fa-css-prefix}-shake {
50 | animation-name: #{$fa-css-prefix}-shake;
51 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
52 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
53 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
54 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
55 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear);
56 | }
57 |
58 | .#{$fa-css-prefix}-spin {
59 | animation-name: #{$fa-css-prefix}-spin;
60 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
61 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
62 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s);
63 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
64 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear);
65 | }
66 |
67 | .#{$fa-css-prefix}-spin-reverse {
68 | --#{$fa-css-prefix}-animation-direction: reverse;
69 | }
70 |
71 | .#{$fa-css-prefix}-pulse,
72 | .#{$fa-css-prefix}-spin-pulse {
73 | animation-name: #{$fa-css-prefix}-spin;
74 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
75 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
76 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
77 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8));
78 | }
79 |
80 | // if agent or operating system prefers reduced motion, disable animations
81 | // see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/
82 | // see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
83 | @media (prefers-reduced-motion: reduce) {
84 | .#{$fa-css-prefix}-beat,
85 | .#{$fa-css-prefix}-bounce,
86 | .#{$fa-css-prefix}-fade,
87 | .#{$fa-css-prefix}-beat-fade,
88 | .#{$fa-css-prefix}-flip,
89 | .#{$fa-css-prefix}-pulse,
90 | .#{$fa-css-prefix}-shake,
91 | .#{$fa-css-prefix}-spin,
92 | .#{$fa-css-prefix}-spin-pulse {
93 | animation-delay: -1ms;
94 | animation-duration: 1ms;
95 | animation-iteration-count: 1;
96 | transition-delay: 0s;
97 | transition-duration: 0s;
98 | }
99 | }
100 |
101 | @keyframes #{$fa-css-prefix}-beat {
102 | 0%, 90% { transform: scale(1); }
103 | 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); }
104 | }
105 |
106 | @keyframes #{$fa-css-prefix}-bounce {
107 | 0% { transform: scale(1,1) translateY(0); }
108 | 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); }
109 | 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); }
110 | 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); }
111 | 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); }
112 | 64% { transform: scale(1,1) translateY(0); }
113 | 100% { transform: scale(1,1) translateY(0); }
114 | }
115 |
116 | @keyframes #{$fa-css-prefix}-fade {
117 | 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); }
118 | }
119 |
120 | @keyframes #{$fa-css-prefix}-beat-fade {
121 | 0%, 100% {
122 | opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4);
123 | transform: scale(1);
124 | }
125 | 50% {
126 | opacity: 1;
127 | transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125));
128 | }
129 | }
130 |
131 | @keyframes #{$fa-css-prefix}-flip {
132 | 50% {
133 | transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg));
134 | }
135 | }
136 |
137 | @keyframes #{$fa-css-prefix}-shake {
138 | 0% { transform: rotate(-15deg); }
139 | 4% { transform: rotate(15deg); }
140 | 8%, 24% { transform: rotate(-18deg); }
141 | 12%, 28% { transform: rotate(18deg); }
142 | 16% { transform: rotate(-22deg); }
143 | 20% { transform: rotate(22deg); }
144 | 32% { transform: rotate(-12deg); }
145 | 36% { transform: rotate(12deg); }
146 | 40%, 100% { transform: rotate(0deg); }
147 | }
148 |
149 | @keyframes #{$fa-css-prefix}-spin {
150 | 0% { transform: rotate(0deg); }
151 | 100% { transform: rotate(360deg); }
152 | }
153 |
154 |
--------------------------------------------------------------------------------
/cheatsheets/cloudinary-multer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Subir Imagenes (Multer & Cloudinary)
3 | image: "/assets/images/cloudinary.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Introducción](#introducción)
10 | - [Crear Cuenta (`Cloudinary`)](#crear-cuenta-cloudinary)
11 | - [Instalación](#instalación)
12 | - [Configuracion (Multer)](#configuracion-multer)
13 | - [Conexion (Cloudinary)](#conexion-cloudinary)
14 | - [Almacenamiento de Fotos (Local)](#almacenamiento-de-fotos-local)
15 | - [Subida a la nube con Cloudinary](#subida-a-la-nube-con-cloudinary)
16 | - [Archivos temporales con Multer (para subir a la nube)](#archivos-temporales-con-multer-para-subir-a-la-nube)
17 |
18 |
19 |
20 | ## Introducción
21 |
22 | Con estas dos herramientas, podremos subir imágenes a Cloudinary con el objetivo de obtener el enlace público para acceder a ellas desde cualquier lugar. Utilizaremos `Multer` para recibir los archivos de fotos y, con la conexión a `Cloudinary`, podremos subirlas.
23 |
24 | ## Crear Cuenta (`Cloudinary`)
25 |
26 | Primero, debemos crear una cuenta en `Cloudinary` para poder subir nuestras fotos. Es recomendable hacerlo utilizando GitHub.
27 |
28 | URL: https://console.cloudinary.com/users/register_free
29 |
30 | ## Instalación
31 |
32 | Para instalar `Multer` y `Cloudinary`, deberemos ejecutar los siguientes comandos en la consola (dentro de un proyecto de Node.js):
33 |
34 | ```bash
35 | npm i cloudinary
36 | npm i multer
37 | ```
38 |
39 | ## Configuracion (Multer)
40 |
41 | Para configurar `Multer`, primero debemos crear una carpeta donde se almacenarán las imágenes localmente (ya sea de forma temporal o permanente). Esta carpeta debe estar ubicada dentro del proyecto, ya sea dentro de la carpeta src o en una carpeta separada.
42 |
43 | La configuración de `Multer` debe estar en el router de la ruta donde deseamos trabajar. Por ejemplo, si queremos gestionar la subida de fotos de perfil, configuraremos `Multer` en el router de usuarios.
44 |
45 | Primero, debemos importar el módulo de `Multer`:
46 |
47 | ```js
48 | import multer from 'multer';
49 | ```
50 |
51 | A continuación, debemos realizar la configuración de `Multer`. El código debería verse así:
52 |
53 | ```js
54 | const __filename = fileURLToPath(import.meta.url);
55 | const __dirname = dirname(__filename);
56 |
57 | // Poner la ubicacion de la carpeta de Uploads correspondiente, en este caso se ubica dentro del SRC
58 | const uploadDir = join(__dirname, "../uploads");
59 |
60 | // Se define donde se va a ubicar el archivo que vamos a subir y el nombre, este se puede modificar, en este caso el nombre que se le va a asignar es la fecha de subida sumado del nombre del archivo original
61 | const storage = multer.diskStorage({
62 | destination: function (req, file, cb) {
63 | cb(null, uploadDir);
64 | },
65 | filename: function (req, file, cb) {
66 | cb(null, `${Date.now()}-${file.originalname}`)
67 | }
68 | });
69 |
70 | // El siguiente filtro es para que se suban unicamente archivos con extensiones especificas. En este caso serian JPEG, PNG y JPG
71 | const fileFilter = (req, file, cb) => {
72 | const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
73 | if (allowedTypes.includes(file.mimetype)) {
74 | cb(null, true);
75 | } else {
76 | cb(new Error('Invalid file type. Only PDF, PNG, JPEG, and JPG files are allowed.'), false);
77 | }
78 | };
79 |
80 | const upload = multer({
81 | storage: storage,
82 | fileFilter: fileFilter
83 | });
84 | ```
85 |
86 | ## Conexion (Cloudinary)
87 |
88 | Para realizar la conexión con `Cloudinary`, debemos acceder a nuestro dashboard personal de `Cloudinary` en el siguiente enlace para obtener nuestras credenciales.
89 |
90 | Desde ahí, podemos acceder a nuestro Cloudname, que se encuentra en el centro de la página, y debemos copiarlo. A continuación, hacemos clic en el botón 'Go to API Keys', ubicado a la derecha del Cloudname, donde encontraremos la API Key y el API Secret, los cuales también debemos copiar.
91 |
92 | En nuestro proyecto, debemos crear un archivo llamado `upload.js`, donde configuraremos toda la conexión.
93 |
94 | ```js
95 | import { v2 as cloudinary } from 'cloudinary';
96 |
97 | cloudinary.config({
98 | cloud_name: "",
99 | api_key: "",
100 | api_secret: ""
101 | });
102 |
103 | export default cloudinary;
104 | ```
105 |
106 | ## Almacenamiento de Fotos (Local)
107 |
108 | Para que los archivos se suban localmente, primero debemos definir desde qué ruta lo haremos, configurándolo en el router correspondiente. El siguiente ejemplo es para la creación de usuarios, pero puede modificarse para otras rutas según sea necesario.
109 |
110 | ```js
111 | // Crear Usuarios
112 | router.post("/registerUsers", upload.single('file'), registerController.registerUsers);
113 | ```
114 |
115 | De esta manera, cuando se suban las imágenes, se guardarán en la carpeta `uploads` que hemos creado.
116 |
117 | Además, desde el controlador también podemos controlar las extensiones de los archivos subidos utilizando el siguiente código:
118 |
119 | ```js
120 | // Crear Usuarios
121 | const imageFile = req.file.path;
122 |
123 | const extension = imageFile.split('.').pop();
124 | const extensionesPermitidas = ['pdf', 'png', 'jpeg', 'jpg'];
125 |
126 | if (!extensionesPermitidas.includes(extension)) {
127 | console.error('Extensión de archivo no permitida');
128 | return res.status(400).send('Error: Extensión de archivo no permitida. Extensiones admitidas: PDF, PNG, JPEG, y JPG');
129 | }
130 | ```
131 |
132 | ## Subida a la nube con Cloudinary
133 |
134 | Para subir las imágenes a `Cloudinary`, debemos importar la configuración desde el archivo que habíamos creado (`upload.js`) utilizando el siguiente código en el controlador donde vamos a trabajar:
135 |
136 | ```js
137 | import cloudinary from '../upload.js';
138 | ```
139 |
140 | Para subir las imágenes a `Cloudinary`, dentro del controlador donde estemos trabajando, debemos usar el `imageFile` que estábamos gestionando con `Multer`. Luego, debemos ejecutar la siguiente función para subir la imagen, la cual nos devolverá un JSON con la información. Con esta información, podemos almacenar la URL de donde se ubicará la imagen:
141 |
142 | ```js
143 | const imageFile = req.file.path;
144 |
145 | const result = await cloudinary.uploader.upload(imageFile, {
146 | folder: 'analisis',
147 | });
148 |
149 | const imageUrl = result.secure_url;
150 | ```
151 |
152 | A continuación, podemos guardar el enlace en la base de datos de la siguiente forma:
153 |
154 | ```js
155 | const query = 'INSERT INTO public.users (imagen) VALUES ($1)';
156 |
157 | await client.query(query, [imageUrl]);
158 | ```
159 |
160 | ## Archivos temporales con Multer (para subir a la nube)
161 |
162 | Para que los archivos sean temporales, simplemente importa el módulo `fs` en el controlador y ejecuta la función `unlinkSync` para eliminar los archivos después de que ya hayan sido procesados. Aquí tienes un ejemplo de cómo hacerlo:
163 |
164 | ```js
165 | import fs from "fs";
166 |
167 | const imageFile = req.file.path;
168 |
169 | const result = await cloudinary.uploader.upload(imageFile, {
170 | folder: 'analisis',
171 | });
172 |
173 | const imageUrl = result.secure_url;
174 |
175 | const query = 'INSERT INTO public.users (imagen) VALUES ($1)';
176 |
177 | await client.query(query, [imageUrl]);
178 |
179 | fs.unlinkSync(imageFile); // Eliminar el archivo local
180 | ```
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from "next";
2 | import Head from "next/head";
3 | import fs from "fs";
4 | import path from "path";
5 | import matter from "gray-matter";
6 | import GitHubButton from "react-github-btn";
7 | import Background from "../components/layout/Background";
8 | import Terminal from "../components/utils/Terminal/Terminal";
9 | import Glassbox from "../components/utils/Glassbox";
10 | import Section from "../components/layout/Section";
11 | import { useWindowSize } from "usehooks-ts";
12 | import Image from "next/image";
13 | import { useMemo } from "react";
14 |
15 | export type CheatsheetType = {
16 | slug: string;
17 | frontmatter: {
18 | title: string;
19 | image: string;
20 | };
21 | };
22 |
23 | const Home: NextPage = ({ cheatsheets }: { cheatsheets: CheatsheetType[] }) => {
24 | const windowSize = useWindowSize();
25 |
26 | const ratio = 20 / 17;
27 |
28 | const imageWidth = useMemo(() => {
29 | if (windowSize.width >= 1900) return 800;
30 | if (windowSize.width >= 1450) return 600;
31 | if (windowSize.width >= 1250) return 400;
32 | if (windowSize.width >= 1000) return 380;
33 | if (windowSize.width >= 800) return 320;
34 | if (windowSize.width >= 500) return 250;
35 | return 200;
36 | }, [windowSize]);
37 |
38 | const imageHeight = useMemo(() => imageWidth / ratio, [imageWidth]);
39 |
40 | return (
41 | <>
42 |
88 | En esta web se encuentra una colección de
89 | "hojas de trucos" a las que se puede
90 | recurrir a la hora de programar en cualquiera de los
91 | lenguajes disponibles. La idea es que ésta sea de
92 | realización colectiva, es decir, que todos (tanto
93 | profesores como alumnos) puedan hacer su aporte a
94 | los cheatsheets, con el objetivo de aumentar la
95 | calidad y la cantidad de la información. Para
96 | aportar algún cambio o un cheatsheet nuevo, es muy
97 | importante que leas el README del repositorio.
98 |
99 |
100 |
101 | Si este repositorio te sirve podés darle una
102 | estrellita{" "}
103 |
56 |
57 | Para saber cómo usarlos y qué hacen, podes utilizar 'man [comando]'
58 | `,
59 | man: `
60 | help
61 |
62 |
63 | Muestra una lista de los comandos disponibles.
64 |
65 | `,
66 | },
67 | about: {
68 | type: "text",
69 | content: `
70 | ¿Qué es <TIC Cheatsheets/>?
71 |
72 |
73 | En esta web se encuentra una colección de
74 | "hojas de trucos" a las que se puede
75 | recurrir a la hora de programar en cualquiera de los
76 | lenguajes disponibles. La idea es que ésta sea de
77 | realización colectiva, es decir, que todos (tanto
78 | profesores como alumnos) puedan hacer su aporte a
79 | los cheatsheets, con el objetivo de aumentar la
80 | calidad y la cantidad de la información. Para
81 | aportar algún cambio o un cheatsheet nuevo, es muy
82 | importante que leas el README del repositorio.
83 | `,
84 | man: `
85 | about
86 |
87 |
88 | Muestra información sobre la web.
89 |
90 | `,
91 | },
92 | github: {
93 | type: "voidAction",
94 | action: () =>
95 | window.open(
96 | "https://github.com/nachovigilante/cheatsheets",
97 | "_blank"
98 | ),
99 | man: `
100 | github
101 |
102 |
103 | Abre el repositorio de la web en una nueva pestaña.
104 |
105 | `,
106 | },
107 | open: {
108 | type: "voidAction",
109 | action: openFile,
110 | args: ["slug"],
111 | man: `
112 | open [slug].md
113 |
114 |
115 | Abre el cheatsheet con el slug indicado (igual que cat).
116 |
117 | `,
118 | usesFileNames: true,
119 | },
120 | cat: {
121 | type: "voidAction",
122 | action: openFile,
123 | args: ["slug"],
124 | man: `
125 | cat [slug].md
126 |
127 |
128 | Abre el cheatsheet con el slug indicado (igual que open).
129 |
130 | `,
131 | usesFileNames: true,
132 | },
133 | export: {
134 | type: "voidAction",
135 | action: (slug: string, validFileNames: string[]) => {
136 | if (!validFileNames.includes(slug)) return false;
137 |
138 | const link = slug.split(".")[0];
139 | const anchor = document.createElement("a");
140 | anchor.href = `/download/${link}.pdf`;
141 | anchor.download = `${link}.pdf`;
142 | document.body.appendChild(anchor);
143 | anchor.click();
144 | return true;
145 | },
146 | args: ["slug"],
147 | man: `
148 | export [slug]
149 |
150 |
151 | Descarga el cheatsheet con el slug indicado.
152 |
153 | `,
154 | usesFileNames: true,
155 | },
156 | cls: {
157 | type: "voidAction",
158 | action: () => {
159 | clearCommands();
160 | return true;
161 | },
162 | preventAdd: true,
163 | man: `
164 | cls
165 |
166 |
167 | Limpia la terminal.
168 |
169 | `,
170 | },
171 | ls: {
172 | type: "text",
173 | content: `
174 |
175 | ${
176 | cheatsheets
177 | .map((c) => `
${c.slug}.md
`)
178 | .join("") || "No hay cheatsheets disponibles"
179 | }
180 |
181 | `,
182 | man: `
183 | ls
184 |
185 |
186 | Muestra una lista de los cheatsheets disponibles.
187 |
188 | `,
189 | },
190 | pwd: {
191 | type: "text",
192 | content: "tic://cheatsheets/",
193 | man: `
194 | pwd
195 |
196 |
197 | Muestra la ruta actual.
198 |
199 | `,
200 | },
201 | code: {
202 | type: "voidAction",
203 | action: () => {
204 | window.open(
205 | "https://vscode.dev/github/nachovigilante/cheatsheets",
206 | "_blank"
207 | );
208 | },
209 | man: `
210 | code
211 |
212 |
213 | Abre el repositorio de la web en VS Code.
214 |
215 | `,
216 | },
217 | shutdown: {
218 | type: "voidAction",
219 | action: () => {
220 | window.open(
221 | "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
222 | "_blank"
223 | );
224 | },
225 | man: `
226 | shutdown
227 |
228 |
229 | Apaga la terminal.
230 |
231 | `,
232 | },
233 | man: {
234 | type: "action",
235 | action: (command: string) => {
236 | console.log("AAAAAAAA");
237 | const commandItemKey = Object.keys(commands).find(
238 | (c) => c === command
239 | );
240 | const commandItem = commands[commandItemKey];
241 | if (commandItem) {
242 | return commandItem.man;
243 | }
244 | return "No se ha encontrado el comando indicado.";
245 | },
246 | args: ["command"],
247 | man: `
248 | man [command]
249 |
250 |
251 | Muestra la documentación del comando indicado.
252 |
253 | `,
254 | },
255 | };
256 |
257 | return commands;
258 | };
259 |
260 | export default useCommandList;
261 |
--------------------------------------------------------------------------------
/cheatsheets/react.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: React
3 | image: "/assets/images/react.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Elementos](#elementos)
10 | - [Sintaxis de un elemento](#sintaxis-de-un-elemento)
11 | - [Atributos de un elemento](#atributos-de-un-elemento)
12 | - [Componentes](#componentes)
13 | - [Fragmentos de React](#fragmentos-de-react)
14 | - [Propiedades](#propiedades)
15 | - [Condicionales](#condicionales)
16 | - [Operadores ternarios](#operadores-ternarios)
17 | - [If](#if)
18 | - [Listas](#listas)
19 | - [Hooks](#hooks)
20 | - [useState](#usestate)
21 | - [useEffect](#useeffect)
22 | - [useContext](#usecontext)
23 | - [useRef](#useref)
24 | - [useMemo](#usememo)
25 | - [useCallback](#usecallback)
26 |
27 |
28 |
29 | ## Elementos
30 |
31 | ### Sintaxis de un elemento
32 |
33 | Los elementos de React se escriben igual que los de HTML, hay algunas diferencias pero dentro de React se puede escribir HTML puro.
34 |
35 | Una diferencia con HTML, por ejemplo, es que no se pueden escribir elementos que no se cierran, como imagenes o saltos de línea.
36 |
37 | Por ejemplo, en HTML una imagen podría usarse así
38 |
39 | ```html
40 |
41 | ```
42 |
43 | Pero en React debe cerrarse ese tag, de esta manera
44 |
45 | ```html
46 |
47 | ```
48 |
49 | ### Atributos de un elemento
50 |
51 | Los atributos en React no pueden contener caracteres especiales, por lo que algunos serán diferentes a los de HTML.
52 |
53 | Un cambio importante en React es el del atributo clase, el cual cambia de `class` a `className`.
54 |
55 | HTML:
56 |
57 | ```html
58 |
59 | ```
60 |
61 | React:
62 |
63 | ```html
64 |
65 | ```
66 |
67 | ## Componentes
68 |
69 | Los componentes de React pueden ser escritos como clases o como funciones.
70 |
71 | Un componente funcional de React se expresa de la siguiente manera
72 |
73 | ```javascript
74 | function App() {
75 | return
Hello world!
;
76 | }
77 | ```
78 |
79 | Los componentes de React deben empezar con una letra mayúscula obligatoriamente y devolver código JSX (HTML de React)
80 |
81 | ### Fragmentos de React
82 |
83 | Los componentes de React deben devolver un único elemento. Por ejemplo, esto no es válido.
84 |
85 | ```javascript
86 | function App() {
87 | return (
88 |
Hello world!
89 |
Hello world!
90 | );
91 | }
92 | ```
93 |
94 | Si se quiere devolver más de un elemento sin encerrarlo en otro, como un div, se debe usar un Fragmento, el cual puede expresarse de 2 maneras.
95 |
96 | ```javascript
97 | function App() {
98 | return (
99 | <>
100 |
113 |
114 | );
115 | }
116 | ```
117 |
118 | ## Propiedades
119 |
120 | Al crear componentes en React, podemos añadirle propiedades con el parametro props, el cual recibe todos los atributos que se le pasan al crearlo en otro componente.
121 |
122 | Por ejemplo, en el componente App puedo crear el componente User y pasar la propiedad nombre para que se muestre ese texto en el otro componente.
123 |
124 | ```javascript
125 | function App() {
126 | return ;
127 | }
128 |
129 | function User(props) {
130 | return
138 | ```
139 |
140 | Hay algunas propiedades que se pasan por defecto, como la propiedad children, la cual contiene los elementos que pusiste adentro del componente.
141 |
142 | Por ejemplo
143 |
144 | ```javascript
145 | function App() {
146 | return (
147 |
148 |
164 | ```
165 |
166 | ## Condicionales
167 |
168 | Los condicionales en React se pueden hacer de 2 maneras, con operadores ternarios o con if.
169 |
170 | ### Operadores ternarios
171 |
172 | Los operadores ternarios se pueden usar para mostrar un componente si se cumple una condición.
173 |
174 | Por ejemplo, si quiero mostrar un componente si el usuario está logueado, puedo usar un operador ternario de la siguiente manera
175 |
176 | ```javascript
177 | function App() {
178 | const isLogged = true;
179 | return
{isLogged ? : }
;
180 | }
181 | ```
182 |
183 | ### If
184 |
185 | Los condicionales if se pueden usar para mostrar un componente si se cumple una condición.
186 |
187 | Por ejemplo, si quiero mostrar un componente si el usuario está logueado, puedo usar un if de la siguiente manera
188 |
189 | ```javascript
190 | function App() {
191 | const isLogged = true;
192 |
193 | if (isLogged) {
194 | return ;
195 | } else {
196 | return ;
197 | }
198 | }
199 | ```
200 |
201 | ## Listas
202 |
203 | Para crear una lista con un array, se debe usar el método map, el cual recibe una función que se ejecutará por cada elemento del array.
204 |
205 | Por ejemplo, si quiero mostrar una lista de usuarios, puedo usar un array de la siguiente manera
206 |
207 | ```javascript
208 | function App() {
209 | const users = ["John Doe", "Jane Doe", "Jack Doe"];
210 |
211 | return (
212 |
217 | );
218 | }
219 | ```
220 |
221 | ## Hooks
222 |
223 | Los hooks son funciones que nos permiten usar características de React sin tener que crear un componente. Acá hay una lista de los hooks mas conocidos.
224 |
225 | ### useState
226 |
227 | El hook useState nos permite crear variables de estado, las cuales se pueden modificar y React se encargará de actualizar el componente.
228 |
229 | Por ejemplo, si quiero crear una variable de estado que se llame count y que empiece en 0, puedo usar el hook useState de la siguiente manera
230 |
231 | ```javascript
232 | function App() {
233 | const [count, setCount] = useState(0);
234 | return (
235 |
236 |
Count: {count}
237 |
238 |
239 | );
240 | }
241 | ```
242 |
243 | ### useEffect
244 |
245 | El hook useEffect nos permite ejecutar código cuando se renderiza el componente o cuando se actualiza una variable de estado.
246 |
247 | Por ejemplo, si quiero ejecutar un código cuando se renderiza el componente, puedo usar el hook useEffect de la siguiente manera
248 |
249 | ```javascript
250 | function App() {
251 | useEffect(() => {
252 | console.log("Se renderizó el componente");
253 | }, []);
254 | return (
255 |
256 |
Count: {count}
257 |
258 |
259 | );
260 | }
261 | ```
262 |
263 | Si quiero ejecutar un código cuando se actualiza una variable de estado, puedo usar el hook useEffect de la siguiente manera
264 |
265 | ```javascript
266 | function App() {
267 | const [count, setCount] = useState(0);
268 | useEffect(() => {
269 | console.log("Se actualizó la variable count");
270 | }, [count]);
271 | return (
272 |
273 |
Count: {count}
274 |
275 |
276 | );
277 | }
278 | ```
279 |
280 | ### useContext
281 |
282 | El hook useContext nos permite usar el contexto de un componente padre en un componente hijo.
283 |
284 | Por ejemplo, si quiero usar el contexto de un componente padre en un componente hijo, puedo usar el hook useContext de la siguiente manera
285 |
286 | ```javascript
287 | const UserContext = React.createContext();
288 |
289 | function App() {
290 | return (
291 |
292 |
293 |
294 | );
295 | }
296 |
297 | function User() {
298 | const user = useContext(UserContext);
299 | return
Hola, {user}
;
300 | }
301 | ```
302 |
303 | ### useRef
304 |
305 | El hook useRef nos permite crear una referencia a un elemento del DOM.
306 |
307 | Por ejemplo, si quiero crear una referencia a un elemento del DOM, puedo usar el hook useRef de la siguiente manera
308 |
309 | ```javascript
310 | function App() {
311 | const inputRef = useRef(null);
312 | return (
313 |
314 |
315 |
316 |
317 | );
318 | }
319 | ```
320 |
321 | ### useMemo
322 |
323 | El hook useMemo nos permite crear una variable que se actualiza solo cuando una variable de estado cambia.
324 |
325 | Por ejemplo, si quiero crear una variable que se actualiza solo cuando el contador cambia, puedo usar el hook useMemo de la siguiente manera
326 |
327 | ```javascript
328 | function App() {
329 | const [count, setCount] = useState(0);
330 | const countDoble = useMemo(() => {
331 | return count * 2;
332 | }, [count]);
333 | return (
334 |
335 |
Count: {count}
336 |
Count doble: {countDoble}
337 |
338 |
339 | );
340 | }
341 | ```
342 |
343 | ### useCallback
344 |
345 | El hook useCallback nos permite crear una función que se actualiza solo cuando una variable de estado cambia.
346 |
347 | Por ejemplo, si quiero crear una función que se actualiza solo cuando el contador cambia, puedo usar el hook useCallback de la siguiente manera
348 |
349 | ```javascript
350 | function App() {
351 | const [count, setCount] = useState(0);
352 | const incrementar = useCallback(() => {
353 | setCount(count + 1);
354 | }, [count]);
355 | return (
356 |
357 |
Count: {count}
358 |
359 |
360 | );
361 | }
362 | ```
363 |
--------------------------------------------------------------------------------
/cheatsheets/express.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: express.js
3 | image: "/assets/images/express.svg"
4 | ---
5 |
6 | ## Índice
7 |
8 | - [Índice](#índice)
9 | - [Instalación](#instalación)
10 | - [Creación de un servidor](#creación-de-un-servidor)
11 | - [Rutas](#rutas)
12 | - [Requests](#requests)
13 | - [Métodos HTTP](#métodos-http)
14 | - [GET](#get)
15 | - [Query parameters](#query-parameters)
16 | - [Path parameters](#path-parameters)
17 | - [POST](#post)
18 | - [Body parameters](#body-parameters)
19 | - [PUT](#put)
20 | - [DELETE](#delete)
21 | - [Responses](#responses)
22 | - [Métodos de respuesta](#métodos-de-respuesta)
23 |
24 |
25 |
26 | ## Instalación
27 |
28 | Para instalar Express deberemos ejecutar el siguiente comando (dentro de un proyecto de Node ya inicializado):
29 |
30 | ```bash
31 | npm install express
32 | ```
33 |
34 | ## Creación de un servidor
35 |
36 | Para crear un servidor con Express deberemos crear un archivo `index.js` y añadir el siguiente código (recordar que para utilizar `import` deben tener la configuración `"type": "module"` en el archivo `package.json`):
37 |
38 | ```js
39 | // Importamos la librería express
40 | import express from "express";
41 | // Creamos el servidor de Express con la configuración estándar básica
42 | const app = express();
43 |
44 | // Asociamos la ruta "/" a la función pasada como segundo parámetro
45 | app.get("/", (req, res) => {
46 | // Esto envía el texto "Hello World!" como respuesta a la HTTP request
47 | res.send("Hello World!");
48 | });
49 |
50 | // Iniciamos el servidor en el puerto 3000
51 | app.listen(3000, () => {
52 | console.log("Example app listening on port 3000!");
53 | });
54 | ```
55 |
56 | Luego se puede ejecutar con el siguiente comando:
57 |
58 | ```bash
59 | node index.js
60 | ```
61 |
62 | ## Rutas
63 |
64 | Las rutas son lo que permiten al cliente utilizar diferentes URLs para hacer uso de diferentes funcionalidades de nuestro programa. Para añadir una ruta a nuestro servidor, simplemente debemos añadir una nueva llamada a `app.get` (o `app.post`, `app.put`, `app.delete`, etc.) con la URL deseada y la función que se ejecutará cuando se realice la HTTP request con el método indicado en esa URL.
65 |
66 | ```js
67 | app.get("/ruta", (req, res) => {
68 | // Esto envía el texto "Hello World!" como respuesta a la HTTP request
69 | res.send("Hello World!");
70 | });
71 | ```
72 |
73 | Con la siguiente HTTP request se ejecutará la función asociada a la ruta `/ruta`:
74 |
75 | ```http
76 | GET http://host:port/ruta HTTP/1.1
77 | ```
78 |
79 | ## Requests
80 |
81 | ### Métodos HTTP
82 |
83 | Los métodos HTTP más comunes son:
84 |
85 | - `GET`: Se utiliza para obtener información del servidor. Toda la información enviada en una petición GET es visible en la URL.
86 | - `POST`: Se utiliza para enviar información al servidor. La información enviada en una petición POST no es visible en la URL, se envía en el cuerpo (_body_) de la petición.
87 | - `PUT`: Se utiliza para actualizar información en el servidor. Se suele enviar información tanto en la URL como en el cuerpo de la petición.
88 | - `DELETE`: Se utiliza para eliminar información del servidor. Al igual que el método `GET`, la información a eliminar se suele enviar en la URL.
89 |
90 | #### GET
91 |
92 | La información enviada en una petición GET es visible en la URL.
93 |
94 | ##### Query parameters
95 |
96 | Una de las formas de envíar datos dentro de la URL, es utilzando los _query parameters_.
97 |
98 | Por ejemplo, si queremos enviar un parámetro `nombre` con el valor `Juan`, la URL sería `http://host:port/ruta?nombre=Juan`.
99 |
100 | Si quereos enviar más de un parámetro, simplemente debemos separarlos con el símbolo `&`. Por ejemplo, `http://host:port/ruta?nombre=Juan&apellido=Perez`.
101 |
102 | ```http
103 | GET http://host:port/ruta?nombre=Juan&apellido=Perez HTTP/1.1
104 | ```
105 |
106 | Para obtener los valores de los _query parameters_ en el servidor, los encontramos en la propiedad `req.query`.
107 |
108 | ```js
109 | app.get("/ruta", (req, res) => {
110 | // Obtenemos el valor del parámetro "nombre"
111 | const nombre = req.query.nombre;
112 | // Obtenemos el valor del parámetro "apellido"
113 | const apellido = req.query.apellido;
114 | // Esto envía el texto "Hello !" como respuesta
115 | res.send(`Hello ${nombre} ${apellido}!`);
116 | });
117 | ```
118 |
119 | ##### Path parameters
120 |
121 | Otra forma de enviar datos dentro de la URL, es utilizando _path parameters_.
122 |
123 | Por ejemplo, si queremos enviar un parámetro `id` con el valor `123`, la URL sería `http://host:port/ruta/123`.
124 |
125 | Si quereos enviar más de un parámetro, simplemente debemos separarlos con el símbolo `/`. Por ejemplo, `http://host:port/ruta/123/Juan`.
126 |
127 | ```http
128 | GET http://host:port/ruta/123/Juan HTTP/1.1
129 | ```
130 |
131 | Para obtener los valores de los _path parameters_ en el servidor. Debemos definir la ruta de una manera especial, indicando el nombre del parámetro precedido de `:`. El nombre del parámetro lo elegimos nosotros, y no afecta a la ruta en la URL.
132 |
133 | Cuando queremos recibir más de un parámetro, simplemente debemos añadir más `/:` seguidos del nombre del parámetro. Y los encontramos en la propiedad `req.params`.
134 |
135 | Es importante tener en cuenta que irán en el mismo orden en el que los hemos definido en la URL.
136 |
137 | ```js
138 | app.get("/ruta/:id/:nombre", (req, res) => {
139 | // Obtenemos el valor del parámetro "id"
140 | const id = req.params.id;
141 | // Obtenemos el valor del parámetro "nombre"
142 | const nombre = req.params.nombre;
143 | // Esto envía el texto "Hello with id !" como respuesta
144 | res.send(`Hello ${nombre} with id ${id}!`);
145 | });
146 | ```
147 |
148 | #### POST
149 |
150 | La información enviada en una petición POST no es visible en la URL, se envía en el cuerpo (_body_) de la petición.
151 |
152 | Cuando se crea un servidor en Express, debemos decidir cómo vamos a recibir la información enviada en una petición POST. En nuestro caso (para la materia Base de Datos), vamos a recibir la información en formato JSON, por lo que debemos añadir el siguiente _middleware_ al servidor:
153 |
154 | ```js
155 | app.use(express.json());
156 | ```
157 |
158 | Esto permite que Express pueda interpretar el cuerpo de la petición como un objeto JSON.
159 |
160 | **Importante**: Si no añadimos este _middleware_, el cuerpo de la petición estaría vacío, y no podremos acceder a los datos enviados en la petición POST.
161 |
162 | ##### Body parameters
163 |
164 | Para enviar información en una petición POST, debemos añadir un objeto JSON en el cuerpo de la petición.
165 |
166 | ```http
167 | POST http://host:port/ruta HTTP/1.1
168 | Content-Type: application/json
169 |
170 | {
171 | "nombre": "Juan",
172 | "apellido": "Perez"
173 | }
174 | ```
175 |
176 | Para obtener los valores de los _body parameters_ en el servidor, los encontramos en la propiedad `req.body`.
177 |
178 | ```js
179 | app.post("/ruta", (req, res) => {
180 | // Obtenemos el valor del parámetro "nombre"
181 | const nombre = req.body.nombre;
182 | // Obtenemos el valor del parámetro "apellido"
183 | const apellido = req.body.apellido;
184 | // Esto envía el texto "Hello !" como respuesta
185 | res.send(`Hello ${nombre} ${apellido}!`);
186 | });
187 | ```
188 |
189 | #### PUT
190 |
191 | Este método es muy similar al método POST, pero se suele utilizar para actualizar información en el servidor.
192 |
193 | ```http
194 | PUT http://host:port/ruta/:id HTTP/1.1
195 | Content-Type: application/json
196 |
197 | {
198 | "nombre": "Juan"
199 | }
200 | ```
201 |
202 | Para obtener los valores de los _body parameters_ en el servidor, los encontramos en la propiedad `req.body`.
203 |
204 | ```js
205 | app.put("/ruta/:id", (req, res) => {
206 | // Obtenemos el valor del parámetro "id"
207 | const id = req.params.id;
208 | // Obtenemos el valor del parámetro "nombre"
209 | const nombre = req.body.nombre;
210 |
211 | // Aquí iría la lógica para actualizar al recurso con el id indicado
212 |
213 | // Esto envía el texto "Hello with id !" como respuesta
214 | res.send(`Hello ${nombre} with id ${id}!`);
215 | });
216 | ```
217 |
218 | #### DELETE
219 |
220 | Este método es muy similar al método GET, pero se suele utilizar para eliminar información en lugar de obtenerla.
221 |
222 | ```http
223 | DELETE http://host:port/ruta/:id HTTP/1.1
224 | ```
225 |
226 | Para obtener los valores de los _path parameters_ en el servidor. Los encontramos en la propiedad `req.params`.
227 |
228 | ```js
229 | app.delete("/ruta/:id", (req, res) => {
230 | // Obtenemos el valor del parámetro "id"
231 | const id = req.params.id;
232 |
233 | // Aquí iría la lógica para eliminar al recurso con el id indicado
234 |
235 | // Esto envía el texto "User with id deleted!" como respuesta
236 | res.send(`User with id ${id} deleted!`);
237 | });
238 | ```
239 |
240 | ## Responses
241 |
242 | A la hora de manejar las respuestas en nuestro servidor, Express utiliza una lógica que puede resultar confusa al principio, pero que es muy potente y flexible.
243 |
244 | La respuesta es primero enviada como parámetro a la función que se ejecuta cuando se realiza la petición. A partir de ahí, podemos utilizar diferentes métodos para modificar la respuesta antes de enviarla al cliente.
245 |
246 | ### Métodos de respuesta
247 |
248 | - `res.send(text)`: Envía una respuesta al cliente. El parámetro puede ser un objeto, un array, un string, un número, o `null`.
249 | - `res.json(data)`: Envía una respuesta JSON al cliente. El parámetro puede ser un objeto, un array, un string, un número, o `null`.
250 | - `res.status(status)`: Establece el código de estado de la respuesta..
251 | - `res.sendStatus(status)`: Establece el código de estado de la respuesta y envía el texto asociado a ese código de estado.
252 |
253 | Por ejemplo, si queremos enviar un objeto JSON como respuesta a una petición GET, podemos hacerlo de la siguiente manera:
254 |
255 | ```js
256 | app.get("/ruta", (req, res) => {
257 | // Creamos un objeto JSON
258 | const data = {
259 | nombre: "Juan",
260 | apellido: "Perez"
261 | };
262 | // Enviamos el objeto JSON como respuesta
263 | res.json(data);
264 | });
265 | ```
266 |
--------------------------------------------------------------------------------