eletrotupi / tcc/ api/src/controllers/users.ts44566618da638909dc860baf5a02025c61568c46
5.6 KB | 229 lines Raw
1
import { Request, Response, NextFunction } from 'express';
2
import {
3
  generateToken
4
} from '@app/services/auth.service';
5

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

8
import {
9
  CreateUserSchema,
10
  UpdateUserSchema,
11
  UpdateUserPreferencesSchema,
12
} from '@app/schemas';
13
import { formatValidationError } from '@app/lib/errors/validationError';
14
import { syncDailyReminderJob } from '@app/services/dailyReminder.sync';
15

16
import {
17
  createUser,
18
  findUserById,
19
  findUserByEmail,
20
  updateUser
21
} from '@app/services/user.service';
22

23
import {
24
  storeActivationCode
25
} from '@app/services/auth.service';
26

27
import {
28
  AuthenticatedRequest
29
} from '@app/middleware/auth';
30

31
import { prisma } from '@app/lib/prisma';
32
import s3 from '@app/lib/s3';
33
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
34
import crypto from 'crypto';
35
import { addMinutes } from 'date-fns';
36
import { User } from '@prisma/client';
37

38
// XXX: I've regreted already
39
interface SingleFileRequest extends AuthenticatedRequest {
40
  file?: any;
41
}
42

43
const withoutSecrets = (user: User) => {
44
  const {
45
    encryptedPassword,
46
    passwordResetToken,
47
    passwordResetExpires,
48
    ...safe
49
  } = user;
50

51
  return safe;
52
};
53

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

59
      if (!parsed.success) {
60
        return res.status(400).json(formatValidationError(parsed.error!));
61
      }
62

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

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

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

72
      const mailQueue = getQueue('mail');
73
      const job = await mailQueue.add(MailJobName.WelcomeEmail, {
74
        userId: user.id,
75
        code: code
76
      });
77

78
      res.status(201).json({
79
        token: jwtToken
80
      });
81
    } catch (err) {
82
      next(err);
83
    }
84
  },
85

86
  update: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
87
    try {
88
      const targetId = Number(req.params.id);
89

90
      if (targetId !== req.userId) {
91
        return res.status(403).json({
92
          error: "Você só pode atualizar o próprio perfil"
93
        });
94
      }
95

96
      const parsed = UpdateUserSchema.safeParse(req.body.user);
97
      if (!parsed.success) {
98
        return res.status(400).json(formatValidationError(parsed.error!));
99
      }
100

101
      const user = await findUserById(targetId);
102

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

109
      const updated = await updateUser({ ...parsed.data, id: targetId });
110

111
      return res.status(200).json({
112
        user: withoutSecrets(updated)
113
      });
114
    } catch (err) {
115
      next(err);
116
    }
117
  },
118

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

125
      const currentUser = await findUserById(Number(req.userId));
126
      if (currentUser?.avatarKey) {
127
        await s3.send(
128
          new DeleteObjectCommand({
129
            Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
130
          })
131
        );
132
      }
133

134
      const user = await prisma.user.update({
135
        where: {
136
          id: currentUser?.id
137
        }, data: {
138
          avatarURL: req.file.location,
139
          avatarKey: req.file.key
140
        }
141
      });
142

143
      res.json({
144
        message: 'File uploaded successfully',
145
        fileLocation: req.file.location,
146
        key: req.file.key,
147
        filename: req.file.originalname,
148
        size: req.file.size,
149
        user: user
150
      });
151
    } catch (err) {
152
      next(err);
153
    }
154
  },
155

156
  updatePreferences: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
157
    try {
158
      const parsed = UpdateUserPreferencesSchema.safeParse(req.body.user);
159
      if (!parsed.success) {
160
        return res.status(400).json(formatValidationError(parsed.error!));
161
      }
162

163
      const user = await prisma.user.update({
164
        where: { id: req.userId },
165
        data: parsed.data,
166
      });
167

168
      await syncDailyReminderJob(user);
169

170
      return res.status(200).json({ user });
171
    } catch (err) {
172
      next(err);
173
    }
174
  },
175

176
  me: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
177
    try {
178
      const user = await prisma.user.findUnique({
179
        where: { id: req.userId! },
180
        select: {
181
          id: true,
182
          email: true,
183
          firstName: true,
184
          lastName: true,
185
          updatedAt: true,
186
          avatarKey: true,
187
          avatarURL: true,
188
          active: true,
189
          pushToken: true,
190
          notificationsEnabled: true,
191
          dailyReminderTime: true,
192
        },
193
      });
194

195
      if (!user) return res.status(404).json({ error: "User not found" });
196

197
      return res.json({ user });
198
    } catch (err) {
199
      next(err);
200
    }
201
  },
202

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

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

210
    await s3.send(
211
      new DeleteObjectCommand({
212
        Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
213
      })
214
    );
215

216
    const user = await prisma.user.update({
217
      where: {
218
        id: req.userId
219
      }, data: {
220
        avatarURL: null,
221
        avatarKey: null
222
      }
223
    });
224

225
    return res.json({
226
      user
227
    })
228
  }
229
}