eletrotupi / tcc/ api/src/controllers/users.ts8f18bacb86dd1b1d21724ad45dd2641371b0d2bf
5.4 KB | 223 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
  issueActivationCode
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 { User } from '@prisma/client';
35

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

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

49
  return safe;
50
};
51

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

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

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

64
      const code = await issueActivationCode(user.id);
65

66
      const mailQueue = getQueue('mail');
67
      const job = await mailQueue.add(MailJobName.WelcomeEmail, {
68
        userId: user.id,
69
        code: code
70
      });
71

72
      res.status(201).json({
73
        token: jwtToken
74
      });
75
    } catch (err) {
76
      next(err);
77
    }
78
  },
79

80
  update: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
81
    try {
82
      const targetId = Number(req.params.id);
83

84
      if (targetId !== req.userId) {
85
        return res.status(403).json({
86
          error: "Você só pode atualizar o próprio perfil"
87
        });
88
      }
89

90
      const parsed = UpdateUserSchema.safeParse(req.body.user);
91
      if (!parsed.success) {
92
        return res.status(400).json(formatValidationError(parsed.error!));
93
      }
94

95
      const user = await findUserById(targetId);
96

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

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

105
      return res.status(200).json({
106
        user: withoutSecrets(updated)
107
      });
108
    } catch (err) {
109
      next(err);
110
    }
111
  },
112

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

119
      const currentUser = await findUserById(Number(req.userId));
120
      if (currentUser?.avatarKey) {
121
        await s3.send(
122
          new DeleteObjectCommand({
123
            Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
124
          })
125
        );
126
      }
127

128
      const user = await prisma.user.update({
129
        where: {
130
          id: currentUser?.id
131
        }, data: {
132
          avatarURL: req.file.location,
133
          avatarKey: req.file.key
134
        }
135
      });
136

137
      res.json({
138
        message: 'File uploaded successfully',
139
        fileLocation: req.file.location,
140
        key: req.file.key,
141
        filename: req.file.originalname,
142
        size: req.file.size,
143
        user: user
144
      });
145
    } catch (err) {
146
      next(err);
147
    }
148
  },
149

150
  updatePreferences: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
151
    try {
152
      const parsed = UpdateUserPreferencesSchema.safeParse(req.body.user);
153
      if (!parsed.success) {
154
        return res.status(400).json(formatValidationError(parsed.error!));
155
      }
156

157
      const user = await prisma.user.update({
158
        where: { id: req.userId },
159
        data: parsed.data,
160
      });
161

162
      await syncDailyReminderJob(user);
163

164
      return res.status(200).json({ user });
165
    } catch (err) {
166
      next(err);
167
    }
168
  },
169

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

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

191
      return res.json({ user });
192
    } catch (err) {
193
      next(err);
194
    }
195
  },
196

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

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

204
    await s3.send(
205
      new DeleteObjectCommand({
206
        Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
207
      })
208
    );
209

210
    const user = await prisma.user.update({
211
      where: {
212
        id: req.userId
213
      }, data: {
214
        avatarURL: null,
215
        avatarKey: null
216
      }
217
    });
218

219
    return res.json({
220
      user
221
    })
222
  }
223
}