eletrotupi / tcc/ api/src/controllers/users.ts2b1f0479416f5033b2f7f43bd11b6ad759f5ec98
5.1 KB | 215 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 { syncDailyReminderJob } from '@app/services/dailyReminder.sync';
14

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

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

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

30
import { prisma } from '@app/lib/prisma';
31
import s3 from '@app/lib/s3';
32
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
33
import crypto from 'crypto';
34
import { addMinutes } from 'date-fns';
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(422).json({
48
          errors: parsed.error!.issues
49
        })
50
      }
51

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

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

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

61
      const mailQueue = getQueue('mail');
62
      const job = await mailQueue.add(MailJobName.WelcomeEmail, {
63
        userId: user.id,
64
        code: code
65
      });
66

67
      res.status(201).json({
68
        token: jwtToken
69
      });
70
    } catch (err) {
71
      next(err);
72
    }
73
  },
74

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

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

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

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

94
      const updated = await updateUser(parsed.data);
95

96
      // TODO: Drop encrypted password & token here
97
      return res.status(200).json({
98
        user: updated
99
      });
100
    } catch (err) {
101
      next(err);
102
    }
103
  },
104

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

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

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

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

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

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

154
      await syncDailyReminderJob(user);
155

156
      return res.status(200).json({ user });
157
    } catch (err) {
158
      next(err);
159
    }
160
  },
161

162
  me: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
163
    try {
164
      const user = await prisma.user.findUnique({
165
        where: { id: req.userId! },
166
        select: {
167
          id: true,
168
          email: true,
169
          firstName: true,
170
          lastName: true,
171
          updatedAt: true,
172
          avatarKey: true,
173
          avatarURL: true,
174
          active: true,
175
          pushToken: true,
176
          notificationsEnabled: true,
177
          dailyReminderTime: true,
178
        },
179
      });
180

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

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

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

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

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

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

211
    return res.json({
212
      user
213
    })
214
  }
215
}