Skip to content

Instantly share code, notes, and snippets.

@danielsp45
Last active June 2, 2024 19:12
Show Gist options
  • Save danielsp45/fb87b00df22bb8ba89dbdb2efe58c6f9 to your computer and use it in GitHub Desktop.
Save danielsp45/fb87b00df22bb8ba89dbdb2efe58c6f9 to your computer and use it in GitHub Desktop.

Cheat Sheet para o teste de EW

Setup do projeto

Criar servidor express.js

Para criar um servidor express.js para servir de API:

npx express-generator --no-view nome-do-projeto

Para criar um servidor express.js a servir páginas HTML com Pug:

npx express-generator --view=pug nome-do-projeto 

Adicionar mongoDB ao projeto

Para adicionar uma base de dados mongo precisamos de criar e rodar um docker container com o dataset fornecido e adaptar o servidor express.js.

Para criar e rodar o container basta correr o script setup-container.py deste gist:

python3 setup-container.py db-name collection-name json-file-path

Atenção: O formato do JSON do dataset fornecido ao script deve ser uma lista de objetos e os id's devem ter o formato de _id.

Depois basta adicionar as seguintes linhas de código ao app.js e alterar o url de db-name para o nome passado ao scritp de setup do container:

var mongoose = require("mongoose");

var mongoDB = "mongodb://127.0.0.1/db-name";
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on("error", console.error.bind(console, "Erro de conexão ao MongoDB"));
db.once("open", () => {
  console.log("Conexão ao MongoDB realizada com sucesso");
});

Setup dos pedidos à DB

Para organizar os modelos e controladores, tal como ensinado nas aulas, basta criar as pastas /models e /controllers. O exemplo de um model será tipo isto:

const mongoose = require('mongoose');

const compositorSchema = new mongoose.Schema({
  id: String,
  nome: String,
  bio: String,
  periodo: String,
  dataNasc: String,
  dataObito: String,
}, { versionKey: false });

module.exports = mongoose.model('compositores', compositorSchema);

E o respetivo controlador:

const Compositor = require('../models/compositor');

module.exports.list = async () => {
  return await Compositor
    .find()
    .exec();
}

module.exports.findById = id => {
  return Compositor
    .findOne({ id: id })
    .exec();
}

module.exports.findByPeriodo = periodo => {
  return Compositor
    .find({ periodo: periodo })
    .exec();
}

module.exports.insert = compositor => {
  return Compositor.create(compositor);
}

module.exports.removeById = id => {
  return Compositor.deleteOne({ id: id });
}

module.exports.update = (id, compositor) => {
  return Compositor.updateOne({ id: id }, compositor);
}

Submissão de ficheiros

Caso queiramos ter um forms com capacidade de submissão de ficheiros, temos de seguir os seguintes passos:

1. Criar uma pasta para guardar os ficheiros submetidos

Para armazenar os ficheiros enviados temporariamente, criaremos uma pasta chamada /uploads na raiz do nosso projeto. Posteriormente, para guardar os ficheiros de forma permanente, criaremos uma pasta chamada /public/filestore.

2. Configurar package multer

Este pedaço de código deve ser adicionado no início do router e configura a utilização do Multer para o upload de ficheiros. O Multer é uma biblioteca Node.js usada para manipular multipart/form-data, o que é essencial para realizar uploads de ficheiros em aplicações web.

var multer = require('multer');
var upload = multer({ dest: 'uploads/' });

Na segunda linha desta configuração, está-se a criar uma instância do Multer que especifica a diretoria onde os ficheiro enviados devem ser armazenados temporariamente antes de serem movidos para o destino final.

3. Criar uma rota capaz de receber esse ficheiro

O seguinte código é uma rota de submissão de ficheiros, configurada para receber um ficheiro enviado pelo cliente, movê-lo para uma localização específica no servidor, e atualizar um arquivo JSON que mantém um registo dos ficheiros submetidos.

/* File submission */
router.post('/upload', upload.single('file'), function (req, res, next) {
    let form = req.body;
    let file = req.file;
    console.log(form);
    console.log(file);
 
    // Move file to another folder
    var oldPath = __dirname + '/../' + file.path;
    var newPath = __dirname + '/../public/filestore/' + file.originalname;
 
    fs.rename(oldPath, newPath, function (err) {
        if (err) {
            res.render('error', { error: err });
            return;
        }
        res.redirect("/");
    });
});
  • O upload.single('myFile') é um middleware fornecido pelo Multer que processa o ficheiro enviado no campo myFile do formulário. Ele anexa a informação do ficheiro processado ao objeto req.file.
  • O var oldPath = __dirname + '/../' + file.path; define o caminho atual do ficheiro temporário.
  • O var newPath = __dirname + '/../public/fileStore/' + file.originalname; define o novo caminho onde o ficheiro será movido.
  • O fs.rename(oldPath, newPath, function (err) { ... }); usa a função rename do módulo fs para mover o ficheiro do caminho temporário para o novo caminho. Se houver um erro durante este processo, uma página de erro é renderizada.

3. Front-end com forms de submissão de ficheiros

Por fim, temos apenas de configurar o código HTML para suportar a submissão de ficheiros:

  .w3-card-4
    header.w3-indigo 
      h1 File Upload 
    form.w3-container(action="/files", method="POST", enctype="multipart/form-data")
      label.w3-text-teal
        b Select file        
      input.w3-input.w3-border.w3-light-grey(type="file", name="myFile")
      label.w3-text-teal
        b Description 
      input.w3-input.w3-border.w3-light-grey(type="text", name="desc")
      input.w3-btn.w3-blue-grey(type="submit", value="Send")

Atenção: O enctype do forms deve ser multipart/form-data de modo a suportar ficheiros e o nome do ficheiro deve ter o mesmo nome usado na rota ("myFile").

Utilizar API com auth wall

Para utilizar uma API com auth wall é necessário fazer pedidos para registar/loggar o utilizador e posteriormente utilizar a cookie fornecida pelo servidor para fazer pedidos à API.

Registo/Login de utilizador

Primeiro é preciso criar uma rota para processar o pedido de login:

router.post('/login', function(req, res, next) {
  axios.post('http://localhost:2204/users/login', req.body)
    .then(resposta => {
      res.cookie('token', resposta.data.token);
      res.redirect('/');
    })
    .catch(erro => {
      res.render('error', { error: erro, message: "Credenciais inválidas" });
    });
});

Neste exemplo, está a ser enviando um pedido POST para '/login' com as credenciais do utilizador. Se as credenciais forem válidas, o servidor enviará de volta um token de autenticação no corpo da resposta. Este token é então armazenado em um cookie chamado 'token'.

Utilização do Token para pedidos

Agora que o utilizador está autenticado e tem um token válido armazenado em um cookie, podemos usá-lo para fazer pedidos à API protegida. Por exemplo, aqui está como se pode fazer um pedido de dados protegidos pela autenticação:

router.get('/ucs', function(req, res, next) {
  var token = '';
  if (req.cookies && req.cookies.token) {
    token = req.cookies.token;
    axios.get('http://localhost:2204/ucs?token=' + token)
      .then(resposta => {
        res.render('uc', { lista: resposta.data });
      })
      .catch(erro => {
        res.render('error', { error: erro, message: "Erro ao obter lista de UCs" });
      });
  }
});

Neste exemplo, verificamos se o cookie 'token' está presente na requisição. Se estiver, recuperamos o token e o anexamos ao pedido GET para '/ucs'. Se o pedido for bem-sucedido, renderizamos a página com a lista de UCs.

import subprocess
import os
import sys
def generate_docker_compose(db_name, collection_name, json_file_path):
json_file_path = os.path.abspath(json_file_path)
docker_compose_template = f"""
version: '3.8'
services:
mongodb:
image: mongo:latest
ports:
- "27017:27017"
depends_on:
- mongo-seed
mongo-seed:
image: mongo:latest
volumes:
- {json_file_path}:/datasets/dataset.json
command: mongoimport --host mongodb -d {db_name} -c {collection_name} --type json --file /datasets/dataset.json --jsonArray
"""
with open("docker-compose.yml", "w") as f:
f.write(docker_compose_template)
def start_container():
subprocess.run(["docker-compose", "up", "-d"])
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python script.py <db_name> <collection_name> <json_file_path>")
sys.exit(1)
db_name = sys.argv[1]
collection_name = sys.argv[2]
json_file_path = sys.argv[3]
generate_docker_compose(db_name, collection_name, json_file_path)
start_container()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment