JulienChapron/simple-address-book

View on GitHub
server/controllers/contactsController.ts

Summary

Maintainability
B
6 hrs
Test Coverage
import { contacts } from "../models/Contact.ts";
import { Bson } from "https://deno.land/x/mongo@v0.29.4/mod.ts";
import { decode as base64Decode } from "https://deno.land/std@0.130.0/encoding/base64.ts";
import { AES } from "https://deno.land/x/god_crypto/aes.ts";
import { v4 } from "https://deno.land/std@0.130.0/uuid/mod.ts";
import hexToBytesArray from '../utils/convertToHex.ts'
///// WARNING -- never show -- WARNING /////
const aes = new AES("nbyZK5E#PE!qsv5M", {
  //mode: "cbc",
  iv: "random 16byte iv",
});
const regexEmail =
  /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const regexMobile = /^((\+)33|0|0033)[1-9](\d{2}){4}$/g;
// @description: ADD single contact
// @route POST /api/contacts
const addContact = async ({
  request,
  response,
}: {
  request: any;
  response: any;
}) => {
  try {
    if (!request.hasBody) {
      response.status = 400;
      response.body = {
        success: false,
        msg: "An error has been occured",
      };
    } else {
      const body = await request.body({ type: "form-data" });
      const data = await body.value.read();
      if (
        !data.fields.firstname.length || !data.fields.lastname.length ||
        !data.fields.mobile.length || !data.fields.email.length
      ) {
        return response.body = {
          success: false,
          msg: "All form fields must be filled in",
          type: "allfields",
        };
      }
      const testEmail = regexEmail.test(
        String(data.fields.email).toLowerCase(),
      );
      const testMobile = regexMobile.test(String(data.fields.mobile.replace(/\s/g, "")));

      if (!testEmail) {
        return response.body = {
          success: false,
          msg: "Incorrect email",
          type: "email",
        };
      }
      if (!testMobile) {
        return response.body = {
          success: false,
          msg: "Incorrect mobile number",
          type: "mobile",
        };
      }
      if (data.fields.avatar) {
        // encoded base64
        let imgBase64 = data.fields.avatar;
        imgBase64 = imgBase64.replace("data:image/jpeg;base64,", "").replace(
          "data:image/png;base64,",
          "",
        ).replace(" ", "+");
        const imgBase64Decoded = base64Decode(imgBase64);
        const avatarId = v4.generate();
        data.fields.avatar = `static/${avatarId}.png`;
        Deno.writeFile(`static/${avatarId}.png`, imgBase64Decoded);
      } else {
        data.fields.avatar = "static/user.jpg";
      }

      // encrypt data
      const firstname = await aes.encrypt(data.fields.firstname);
      data.fields.firstname = firstname.hex();
      const lastname = await aes.encrypt(data.fields.lastname);
      data.fields.lastname = lastname.hex();
      const email = await aes.encrypt(data.fields.email);
      data.fields.email = email.hex();
      const mobile = await aes.encrypt(data.fields.mobile);
      data.fields.mobile = mobile.hex();
      const contact = data.fields;
      await contacts.insertOne(contact);
      response.status = 201;
      response.body = {
        success: true,
        data: contact,
      };
    }
  } catch (err) {
    response.body = {
      success: false,
      msg: err.toString(),
    };
  }
};

// DESC: DELETE single contact
// METHOD: DELETE /api/contact/:id
const deleteContact = async ({
  params,
  request,
  response,
}: {
  params: { id: string };
  request: any;
  response: any;
}) => {
  try {
    const body = await request.body();
    const url = await body.value;
    await contacts.delete(
      { _id: new Bson.ObjectId(params.id) },
    );
    if (url !== "static/user.jpg") {
      await Deno.remove(url);
    }
    response.status = 200;
    response.body = {
      success: true,
      data: "this contact is deleted",
    };
  } catch (err) {
    response.body = {
      success: false,
      msg: err.toString(),
    };
  }
};

// DESC: GET single contact
// METHOD: GET /api/contact/:id
const getContact = async ({
  params,
  response,
}: {
  params: { id: string };
  response: any;
}) => {
  try {
    const getContact = await contacts.findOne(
      { _id: new Bson.ObjectId(params.id) },
    );
    response.status = 200;
    response.body = {
      success: true,
      data: getContact,
    };
  } catch (err) {
    response.body = {
      success: false,
      msg: err.toString(),
    };
  }
};

// DESC: GET all contacts
// METHOD: GET /api/contacts
const getContacts = async ({ response, params }: {
  response: any;
  params: { id: string };
}) => {
  const getContacts = await contacts.find({ userId: params.id }).toArray();
  interface contact {
    email: any;
    lastname: any;
    firstname: any;
    mobile: any;
  }
  getContacts.forEach(async (contact: contact) => {
    const email = aes.decrypt(new Uint8Array(hexToBytesArray(contact.email)));
    const lastname = aes.decrypt(
      new Uint8Array(hexToBytesArray(contact.lastname)),
    );
    const firstname = aes.decrypt(
      new Uint8Array(hexToBytesArray(contact.firstname)),
    );
    const mobile = aes.decrypt(new Uint8Array(hexToBytesArray(contact.mobile)));
    Promise.all([email, firstname, lastname, mobile]).then((values) => {
      contact.email = values[0].toString();
      contact.firstname = values[1].toString();
      contact.lastname = values[2].toString();
      contact.mobile = values[3].toString();
    });
  });
  if (getContacts) {
    response.status = 200;
    response.body = {
      success: true,
      data: await getContacts,
    };
  } else {
    response.status = 404;
    response.body = {
      success: false,
      msg: "No contacts found",
    };
  }
};

// DESC: UPDATE single contact
// METHOD: PUT /api/contact/:id
const updateContact = async ({
  params,
  request,
  response,
}: {
  params: { id: string };
  request: any;
  response: any;
}) => {
  try {
    const body = await request.body({ type: "form-data" });
    const data = await body.value.read();
    // test fields all filled
    if (
      !data.fields.firstname.length || !data.fields.lastname.length ||
      !data.fields.mobile.length
    ) {
      return response.body = {
        success: false,
        msg: "All form fields must be filled in",
        type: "allfields",
      };
    }
    const testEmail = regexEmail.test(
      String(data.fields.email).toLowerCase(),
    );
    if (!testEmail) {
      return response.body = {
        success: false,
        msg: "Incorrect email",
        error: "WARNING: Something not so bad happened",
        type: "email",
      };
    }
    if (data.fields.avatar) {
      if (
        data.fields.avatar.includes("data:image/jpeg;base64") ||
        data.fields.avatar.includes("data:image/png;base64")
      ) {
        let imgBase64 = data.fields.avatar;
        imgBase64 = imgBase64.replace("data:image/jpeg;base64,", "").replace(
          "data:image/png;base64,",
          "",
        ).replace(" ", "+");
        const imgBase64Decoded = base64Decode(imgBase64);
        const avatarId = v4.generate();
        data.fields.avatar = `static/${avatarId}.png`;
        Deno.writeFile(`static/${avatarId}.png`, imgBase64Decoded);
      }
    }
    if (!regexMobile.test(String(data.fields.mobile.replace(/\s/g, "")))) {
      return response.body = {
        success: false,
        msg: "Incorrect mobile number",
        type: "mobile",
      };
    }
    const firstname = await aes.encrypt(data.fields.firstname);
    data.fields.firstname = firstname.hex();
    const lastname = await aes.encrypt(data.fields.lastname);
    data.fields.lastname = lastname.hex();
    const email = await aes.encrypt(data.fields.email);
    data.fields.email = email.hex();
    const mobile = await aes.encrypt(data.fields.mobile);
    data.fields.mobile = mobile.hex();

    const contact = data.fields;
    await contacts.updateOne(
      { _id: new Bson.ObjectId(params.id) },
      { $set: contact },
    );

    const updatedContact = await contacts.findOne({
      _id: new Bson.ObjectId(params.id),
    });
    response.status = 200;
    response.body = {
      success: true,
      data: updatedContact,
    };
  } catch (err) {
    response.body = {
      success: false,
      msg: err.toString(),
    };
  }
};

export { addContact, deleteContact, getContact, getContacts, updateContact };