eletrotupi / tcc / api/src/controllers/users.ts master
3.9 KB Raw
import { Request, Response, NextFunction } from 'express';
import {
  generateToken
} from '@app/services/auth.service';

import { getQueue, MailJobName } from '@app/lib/queue';

import {
  CreateUserSchema,
  UpdateUserSchema
} from '@app/schemas';

import {
  createUser,
  findUserById,
  findUserByEmail,
  updateUser
} from '@app/services/user.service';

import {
  storeActivationCode
} from '@app/services/auth.service';

import {
  AuthenticatedRequest
} from '@app/middleware/auth';

import { prisma } from '@app/lib/prisma';
import s3 from '@app/lib/s3';
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
import crypto from 'crypto';
import { addMinutes } from 'date-fns';

// XXX: I've regreted already
interface SingleFileRequest extends AuthenticatedRequest {
  file?: any;
}

export const UsersController = {
  create: async (req: Request, res: Response, next: NextFunction) => {
    try {
      const parsed = CreateUserSchema.safeParse(req.body.user);

      if (!parsed.success) {
        return res.status(422).json({
          errors: parsed.error!.issues
        })
      }

      const user = await createUser(parsed.data);
      const jwtToken = generateToken(user.id, user.email);

      const code = [...Array(6)].map(() => crypto.randomInt(9))
        .join("");

      const expiresAt = addMinutes(new Date(), 5);
      await storeActivationCode(user.id, code, expiresAt);

      const mailQueue = getQueue('mail');
      const job = await mailQueue.add(MailJobName.WelcomeEmail, {
        userId: user.id,
        code: code
      });

      res.status(201).json({
        token: jwtToken
      });
    } catch (err) {
      next(err);
    }
  },

  update: async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { id } = req.params;

      const parsed = UpdateUserSchema.safeParse(req.body.user);
      if (!parsed.success) {
        return res.status(400).json({
          errors: parsed.error!.issues
        });
      }

      const user = await findUserById(Number(id));

      if (!user) {
        return res.status(404).json({
          error: "Usuário não encontrado"
        });
      }

      const updated = await updateUser(parsed.data);

      // TODO: Drop encrypted password & token here
      return res.status(200).json({
        user: updated
      });
    } catch (err) {
      next(err);
    }
  },

  updateAvatar: async (req: SingleFileRequest, res: Response, next: NextFunction) => {
    try {
      if (!req.file) {
        return res.status(400).json({ error: 'No file uploaded' });
      }

      const currentUser = await findUserById(Number(req.userId));
      if (currentUser?.avatarKey) {
        await s3.send(
          new DeleteObjectCommand({
            Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
          })
        );
      }

      const user = await prisma.user.update({
        where: {
          id: currentUser?.id
        }, data: {
          avatarURL: req.file.location,
          avatarKey: req.file.key
        }
      });

      res.json({
        message: 'File uploaded successfully',
        fileLocation: req.file.location,
        key: req.file.key,
        filename: req.file.originalname,
        size: req.file.size,
        user: user
      });
    } catch (err) {
      next(err);
    }
  },

  removeAvatar: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    const currentUser = await findUserById(Number(req.userId));

    if (currentUser && currentUser?.avatarKey == null) {
      return res.json({ user: currentUser })
    }

    await s3.send(
      new DeleteObjectCommand({
        Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
      })
    );

    const user = await prisma.user.update({
      where: {
        id: req.userId
      }, data: {
        avatarURL: null,
        avatarKey: null
      }
    });

    return res.json({
      user
    })
  }
}