eletrotupi / tcc/ api/src/controllers/users.tsc901c35af9c84b6a29e5185545ced1129f33b8f4
5.4 KB | 214 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 { publicUser, publicUserSelect } from '@app/lib/serializers/user';
33
import s3 from '@app/lib/s3';
34
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
35

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

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

46
      if (!parsed.success) {
47
        return res.status(400).json(formatValidationError(parsed.error!));
48
      }
49

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

53
      const code = await issueActivationCode(user.id);
54

55
      const mailQueue = getQueue('mail');
56
      const job = await mailQueue.add(MailJobName.WelcomeEmail, {
57
        userId: user.id,
58
        code: code
59
      });
60

61
      res.status(201).json({
62
        token: jwtToken,
63
        user: publicUser(user),
64
      });
65
    } catch (err) {
66
      next(err);
67
    }
68
  },
69

70
  update: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
71
    try {
72
      const targetId = Number(req.params.id);
73

74
      if (targetId !== req.userId) {
75
        return res.status(403).json({
76
          error: "Você só pode atualizar o próprio perfil"
77
        });
78
      }
79

80
      const parsed = UpdateUserSchema.safeParse(req.body.user);
81
      if (!parsed.success) {
82
        return res.status(400).json(formatValidationError(parsed.error!));
83
      }
84

85
      const user = await findUserById(targetId);
86

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

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

95
      return res.status(200).json({
96
        user: publicUser(updated)
97
      });
98
    } catch (err) {
99
      next(err);
100
    }
101
  },
102

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

109
      const currentUser = await findUserById(Number(req.userId));
110
      if (currentUser?.avatarKey) {
111
        await s3.send(
112
          new DeleteObjectCommand({
113
            Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
114
          })
115
        );
116
      }
117

118
      const user = await prisma.user.update({
119
        where: {
120
          id: currentUser?.id
121
        }, data: {
122
          avatarURL: req.file.location,
123
          avatarKey: req.file.key
124
        }
125
      });
126

127
      res.json({
128
        message: 'File uploaded successfully',
129
        fileLocation: req.file.location,
130
        key: req.file.key,
131
        filename: req.file.originalname,
132
        size: req.file.size,
133
        user: publicUser(user)
134
      });
135
    } catch (err) {
136
      next(err);
137
    }
138
  },
139

140
  updatePreferences: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
141
    try {
142
      const parsed = UpdateUserPreferencesSchema.safeParse(req.body.user);
143
      if (!parsed.success) {
144
        return res.status(400).json(formatValidationError(parsed.error!));
145
      }
146

147
      const user = await prisma.user.update({
148
        where: { id: req.userId },
149
        data: parsed.data,
150
      });
151

152
      await syncDailyReminderJob(user);
153

154
      return res.status(200).json({ user: publicUser(user) });
155
    } catch (err) {
156
      next(err);
157
    }
158
  },
159

160
  completeOnboarding: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
161
    try {
162
      const user = await prisma.user.update({
163
        where: { id: req.userId },
164
        data: { onboardingSeenAt: new Date() },
165
      });
166

167
      return res.status(200).json({ user: publicUser(user) });
168
    } catch (err) {
169
      next(err);
170
    }
171
  },
172

173
  me: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
174
    try {
175
      const user = await prisma.user.findUnique({
176
        where: { id: req.userId! },
177
        select: publicUserSelect,
178
      });
179

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

182
      return res.json({ user });
183
    } catch (err) {
184
      next(err);
185
    }
186
  },
187

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

191
    if (currentUser && currentUser?.avatarKey == null) {
192
      return res.json({ user: publicUser(currentUser) })
193
    }
194

195
    await s3.send(
196
      new DeleteObjectCommand({
197
        Bucket: process.env.S3_BUCKET!, Key: currentUser?.avatarKey!
198
      })
199
    );
200

201
    const user = await prisma.user.update({
202
      where: {
203
        id: req.userId
204
      }, data: {
205
        avatarURL: null,
206
        avatarKey: null
207
      }
208
    });
209

210
    return res.json({
211
      user: publicUser(user)
212
    })
213
  }
214
}