diff --git a/backend/src/config/auth.ts b/backend/src/config/auth.ts index 6f8c5fd..706f3d5 100644 --- a/backend/src/config/auth.ts +++ b/backend/src/config/auth.ts @@ -1,6 +1,6 @@ export default { secret: process.env.JWT_SECRET || "mysecret", - expiresIn: "15m", + expiresIn: "15d", refreshSecret: process.env.JWT_REFRESH_SECRET || "myanothersecret", refreshExpiresIn: "7d" }; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index e1c8a19..06d329d 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -27,7 +27,7 @@ export const index = async (req: Request, res: Response): Promise => { }; export const store = async (req: Request, res: Response): Promise => { - const { email, password, name, profile } = req.body; + const { email, password, name, profile, queueIds } = req.body; if ( req.url === "/signup" && @@ -42,7 +42,8 @@ export const store = async (req: Request, res: Response): Promise => { email, password, name, - profile + profile, + queueIds }); const io = getIO(); diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index 9903d7e..b62e840 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -8,6 +8,7 @@ import ContactCustomField from "../models/ContactCustomField"; import Message from "../models/Message"; import Queue from "../models/Queue"; import WhatsappQueue from "../models/WhatsappQueue"; +import UserQueue from "../models/UserQueue"; // eslint-disable-next-line const dbConfig = require("../config/database"); @@ -24,7 +25,8 @@ const models = [ ContactCustomField, Setting, Queue, - WhatsappQueue + WhatsappQueue, + UserQueue ]; sequelize.addModels(models); diff --git a/backend/src/database/migrations/20210108204708-associate-users-queue.ts b/backend/src/database/migrations/20210108204708-associate-users-queue.ts new file mode 100644 index 0000000..d92496a --- /dev/null +++ b/backend/src/database/migrations/20210108204708-associate-users-queue.ts @@ -0,0 +1,28 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("UserQueues", { + userId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + queueId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("UserQueues"); + } +}; diff --git a/backend/src/models/Queue.ts b/backend/src/models/Queue.ts index 1178329..65d3548 100644 --- a/backend/src/models/Queue.ts +++ b/backend/src/models/Queue.ts @@ -10,6 +10,8 @@ import { Unique, BelongsToMany } from "sequelize-typescript"; +import User from "./User"; +import UserQueue from "./UserQueue"; import Whatsapp from "./Whatsapp"; import WhatsappQueue from "./WhatsappQueue"; @@ -39,6 +41,9 @@ class Queue extends Model { @BelongsToMany(() => Whatsapp, () => WhatsappQueue) whatsapps: Array; + + @BelongsToMany(() => User, () => UserQueue) + users: Array; } export default Queue; diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts index 80d94cf..8de4375 100644 --- a/backend/src/models/Ticket.ts +++ b/backend/src/models/Ticket.ts @@ -14,6 +14,7 @@ import { import Contact from "./Contact"; import Message from "./Message"; +import Queue from "./Queue"; import User from "./User"; import Whatsapp from "./Whatsapp"; @@ -64,6 +65,13 @@ class Ticket extends Model { @BelongsTo(() => Whatsapp) whatsapp: Whatsapp; + @ForeignKey(() => Queue) + @Column + queueId: number; + + @BelongsTo(() => Queue) + queue: Queue; + @HasMany(() => Message) messages: Message[]; } diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index fe840fc..9be664b 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -10,10 +10,13 @@ import { PrimaryKey, AutoIncrement, Default, - HasMany + HasMany, + BelongsToMany } from "sequelize-typescript"; import { hash, compare } from "bcryptjs"; import Ticket from "./Ticket"; +import Queue from "./Queue"; +import UserQueue from "./UserQueue"; @Table class User extends Model { @@ -51,6 +54,9 @@ class User extends Model { @HasMany(() => Ticket) tickets: Ticket[]; + @BelongsToMany(() => Queue, () => UserQueue) + queues: Queue[]; + @BeforeUpdate @BeforeCreate static hashPassword = async (instance: User): Promise => { diff --git a/backend/src/models/UserQueue.ts b/backend/src/models/UserQueue.ts new file mode 100644 index 0000000..17528c2 --- /dev/null +++ b/backend/src/models/UserQueue.ts @@ -0,0 +1,29 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + ForeignKey +} from "sequelize-typescript"; +import Queue from "./Queue"; +import User from "./User"; + +@Table +class UserQueue extends Model { + @ForeignKey(() => User) + @Column + userId: number; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default UserQueue; diff --git a/backend/src/services/UserServices/CreateUserService.ts b/backend/src/services/UserServices/CreateUserService.ts index c48b6d0..930036a 100644 --- a/backend/src/services/UserServices/CreateUserService.ts +++ b/backend/src/services/UserServices/CreateUserService.ts @@ -7,6 +7,7 @@ interface Request { email: string; password: string; name: string; + queueIds?: number[]; profile?: string; } @@ -21,6 +22,7 @@ const CreateUserService = async ({ email, password, name, + queueIds = [], profile = "admin" }: Request): Promise => { const schema = Yup.object().shape({ @@ -47,18 +49,26 @@ const CreateUserService = async ({ throw new AppError(err.message); } - const user = await User.create({ - email, - password, - name, - profile - }); + const user = await User.create( + { + email, + password, + name, + profile + }, + { include: ["queues"] } + ); + + await user.$set("queues", queueIds); + + await user.reload(); const serializedUser = { id: user.id, name: user.name, email: user.email, - profile: user.profile + profile: user.profile, + queues: user.queues }; return serializedUser; diff --git a/backend/src/services/UserServices/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts index 24bd8f0..fb8a202 100644 --- a/backend/src/services/UserServices/ListUsersService.ts +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -1,4 +1,5 @@ import { Sequelize, Op } from "sequelize"; +import Queue from "../../models/Queue"; import User from "../../models/User"; interface Request { @@ -19,8 +20,8 @@ const ListUsersService = async ({ const whereCondition = { [Op.or]: [ { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), + "$User.name$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("User.name")), "LIKE", `%${searchParam.toLowerCase()}%` ) @@ -33,10 +34,13 @@ const ListUsersService = async ({ const { count, rows: users } = await User.findAndCountAll({ where: whereCondition, - attributes: ["name", "id", "email", "profile"], + attributes: ["name", "id", "email", "profile", "createdAt"], limit, offset, - order: [["createdAt", "DESC"]] + order: [["createdAt", "DESC"]], + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] } + ] }); const hasMore = count > offset + users.length; diff --git a/backend/src/services/UserServices/ShowUserService.ts b/backend/src/services/UserServices/ShowUserService.ts index bba0242..3052a2e 100644 --- a/backend/src/services/UserServices/ShowUserService.ts +++ b/backend/src/services/UserServices/ShowUserService.ts @@ -1,9 +1,13 @@ import User from "../../models/User"; import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; const ShowUserService = async (id: string | number): Promise => { const user = await User.findByPk(id, { - attributes: ["name", "id", "email", "profile", "tokenVersion"] + attributes: ["name", "id", "email", "profile", "tokenVersion"], + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] } + ] }); if (!user) { diff --git a/backend/src/services/UserServices/UpdateUserService.ts b/backend/src/services/UserServices/UpdateUserService.ts index e548645..114d557 100644 --- a/backend/src/services/UserServices/UpdateUserService.ts +++ b/backend/src/services/UserServices/UpdateUserService.ts @@ -1,13 +1,14 @@ import * as Yup from "yup"; import AppError from "../../errors/AppError"; -import User from "../../models/User"; +import ShowUserService from "./ShowUserService"; interface UserData { email?: string; password?: string; name?: string; profile?: string; + queueIds?: number[]; } interface Request { @@ -26,14 +27,7 @@ const UpdateUserService = async ({ userData, userId }: Request): Promise => { - const user = await User.findOne({ - where: { id: userId }, - attributes: ["name", "id", "email", "profile"] - }); - - if (!user) { - throw new AppError("ERR_NO_USER_FOUND", 404); - } + const user = await ShowUserService(userId); const schema = Yup.object().shape({ name: Yup.string().min(2), @@ -42,7 +36,7 @@ const UpdateUserService = async ({ password: Yup.string() }); - const { email, password, profile, name } = userData; + const { email, password, profile, name, queueIds = [] } = userData; try { await schema.validate({ email, password, profile, name }); @@ -57,11 +51,16 @@ const UpdateUserService = async ({ name }); + await user.$set("queues", queueIds); + + await user.reload(); + const serializedUser = { id: user.id, name: user.name, email: user.email, - profile: user.profile + profile: user.profile, + queues: user.queues }; return serializedUser; diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts index fc2f1b2..1c2883b 100644 --- a/backend/src/services/WhatsappService/CreateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -18,7 +18,7 @@ interface Response { const CreateWhatsAppService = async ({ name, status = "OPENING", - queueIds, + queueIds = [], isDefault = false }: Request): Promise => { const schema = Yup.object().shape({ @@ -64,14 +64,19 @@ const CreateWhatsAppService = async ({ } } - const whatsapp = await Whatsapp.create({ - name, - status, - isDefault - }); + const whatsapp = await Whatsapp.create( + { + name, + status, + isDefault + }, + { include: ["queues"] } + ); await whatsapp.$set("queues", queueIds); + await whatsapp.reload(); + return { whatsapp, oldDefaultWhatsapp }; }; diff --git a/backend/src/services/WhatsappService/ShowWhatsAppService.ts b/backend/src/services/WhatsappService/ShowWhatsAppService.ts index 6a203a9..ea4a83b 100644 --- a/backend/src/services/WhatsappService/ShowWhatsAppService.ts +++ b/backend/src/services/WhatsappService/ShowWhatsAppService.ts @@ -1,8 +1,13 @@ import Whatsapp from "../../models/Whatsapp"; import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; const ShowWhatsAppService = async (id: string | number): Promise => { - const whatsapp = await Whatsapp.findByPk(id); + const whatsapp = await Whatsapp.findByPk(id, { + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] } + ] + }); if (!whatsapp) { throw new AppError("ERR_NO_WAPP_FOUND", 404); diff --git a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts index 04c3c72..f24fbe1 100644 --- a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts @@ -3,6 +3,7 @@ import { Op } from "sequelize"; import AppError from "../../errors/AppError"; import Whatsapp from "../../models/Whatsapp"; +import ShowWhatsAppService from "./ShowWhatsAppService"; interface WhatsappData { name?: string; @@ -50,13 +51,8 @@ const UpdateWhatsAppService = async ({ } } - const whatsapp = await Whatsapp.findOne({ - where: { id: whatsappId } - }); + const whatsapp = await ShowWhatsAppService(whatsappId); - if (!whatsapp) { - throw new AppError("ERR_NO_WAPP_FOUND", 404); - } await whatsapp.update({ name, status, @@ -66,6 +62,8 @@ const UpdateWhatsAppService = async ({ await whatsapp.$set("queues", queueIds); + await whatsapp.reload(); + return { whatsapp, oldDefaultWhatsapp }; };