From 6c5bc560130561c42577a109a898e682a5094daa Mon Sep 17 00:00:00 2001 From: Fabio Brasileiro Date: Mon, 23 Sep 2024 21:30:11 -0400 Subject: [PATCH] feat(createUnitInCompany/createUser): add new authController and create Controller --- .../migrations/20240924005213_/migration.sql | 23 +++++++ prisma/schema.prisma | 21 +++--- src/@types/express.d.ts | 1 + src/app.ts | 5 ++ src/controllers/authController.ts | 67 +++++++++++++++---- src/controllers/createUnitInCompany.ts | 48 +++++++++++++ src/controllers/createUserController.ts | 65 ++++++++++++++++++ src/routes/createUnitRoutes.ts | 11 +++ src/routes/createUserRoutes.ts | 11 +++ src/services/companyService.ts | 7 +- src/utils/validators.ts | 9 +++ 11 files changed, 244 insertions(+), 24 deletions(-) create mode 100644 prisma/migrations/20240924005213_/migration.sql create mode 100644 src/controllers/createUnitInCompany.ts create mode 100644 src/controllers/createUserController.ts create mode 100644 src/routes/createUnitRoutes.ts create mode 100644 src/routes/createUserRoutes.ts create mode 100644 src/utils/validators.ts diff --git a/prisma/migrations/20240924005213_/migration.sql b/prisma/migrations/20240924005213_/migration.sql new file mode 100644 index 0000000..86edfbe --- /dev/null +++ b/prisma/migrations/20240924005213_/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - A unique constraint covering the columns `[name]` on the table `Company` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[document]` on the table `Company` will be added. If there are existing duplicate values, this will fail. + - Added the required column `document` to the `Company` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Company" ADD COLUMN "document" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "companyId" INTEGER; + +-- CreateIndex +CREATE UNIQUE INDEX "Company_name_key" ON "Company"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "Company_document_key" ON "Company"("document"); + +-- AddForeignKey +ALTER TABLE "User" ADD CONSTRAINT "User_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6188007..e628ad0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -24,18 +24,14 @@ model Product { } model Company { - id Int @id @default(autoincrement()) - name String + id Int @id @default(autoincrement()) + name String @unique + document String @unique // Certifique-se de que esta linha existe + users User[] // Relação com os usuários units Unit[] products Product[] } -model Unit { - id Int @id @default(autoincrement()) - name String - company Company @relation(fields: [companyId], references: [id]) - companyId Int -} model User { id Int @id @default(autoincrement()) @@ -43,6 +39,15 @@ model User { name String password String role UserRole + company Company? @relation(fields: [companyId], references: [id]) + companyId Int? +} + +model Unit { + id Int @id @default(autoincrement()) + name String + company Company @relation(fields: [companyId], references: [id]) + companyId Int } enum UserRole { diff --git a/src/@types/express.d.ts b/src/@types/express.d.ts index 2de3f15..2cc65dc 100644 --- a/src/@types/express.d.ts +++ b/src/@types/express.d.ts @@ -5,6 +5,7 @@ declare global { namespace Express { interface Request { user?: { + id: any role: UserRole // Adicione outras propriedades do usuário, se necessário } diff --git a/src/app.ts b/src/app.ts index e4f7213..599cccf 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,6 +3,8 @@ import express from 'express' import authRoutes from './routes/authRoutes.js' import paymentRoutes from './routes/paymentRoutes.js' import productRoutes from './routes/productRoutes.js' +import createUserRouter from './routes/createUserRoutes.js' +import createUnitRouter from './routes/createUnitRoutes.js' const app = express() @@ -18,5 +20,8 @@ app.get('/', (_req, res) => { app.use('/api/products', productRoutes) app.use('/api/payments', paymentRoutes) app.use('/api', authRoutes) // Adicione '/api' como prefixo para as rotas de autenticação +app.use('/api', createUserRouter) // Adicione '/api' como prefixo para as rotas de autenticação +app.use('/api', createUnitRouter) // Adicione '/api' como prefixo para as rotas de autenticação + export default app diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index c507a63..1e73bf2 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -1,13 +1,14 @@ -import { PrismaClient, User } from '@prisma/client' +import { PrismaClient } from '@prisma/client' import bcrypt from 'bcrypt' -import type { Request, Response } from 'express' import jwt from 'jsonwebtoken' +import type { Request, Response } from 'express' +import * as companyService from '../services/companyService' const prisma = new PrismaClient() const saltRounds = 10 export const register = async (req: Request, res: Response) => { - const { email, password, name, role } = req.body + const { email, password, name, role, companyName, companyDocument } = req.body try { // Verificar se o usuário já existe @@ -19,33 +20,63 @@ export const register = async (req: Request, res: Response) => { return res.status(400).json({ message: 'User already exists' }) } + // Verificar se a empresa já existe + const existingCompany = await prisma.company.findUnique({ + where: { name: companyName, document: companyDocument }, + }) + + let companyId: number + + if (!existingCompany) { + // Criar a empresa + const newCompany = await companyService.createCompany({ + name: companyName, + document: companyDocument, // Documento da empresa (CNPJ ou outro) + }) + + companyId = newCompany.id + } else { + companyId = existingCompany.id + } + // Criptografar a senha const hashedPassword = await bcrypt.hash(password, saltRounds) - // Criar um novo usuário + // Criar um novo usuário e associá-lo à empresa const newUser = await prisma.user.create({ data: { email, password: hashedPassword, name, - role, // Supondo que role é uma propriedade opcional + role: role || 'ADMIN', // O primeiro usuário será ADMIN + company: { connect: { id: companyId } }, // Associa o usuário à empresa }, }) - res - .status(201) - .json({ message: 'User created successfully', user: newUser }) + // Gerar token JWT + const token = jwt.sign( + { id: newUser.id, email: newUser.email, role: newUser.role }, + process.env.JWT_SECRET || 'your_jwt_secret', + { expiresIn: '1d' } + ) + + res.status(201).json({ + message: 'User and company created successfully', + user: newUser, + token, + }) } catch (error) { console.error(error) res.status(500).json({ message: 'Server error' }) } } + export const login = async (req: Request, res: Response) => { const { email, password } = req.body try { - // Encontrar o usuário pelo email + // Verificar se o usuário existe const user = await prisma.user.findUnique({ where: { email }, }) @@ -54,24 +85,32 @@ export const login = async (req: Request, res: Response) => { return res.status(401).json({ message: 'Invalid credentials' }) } - // Verificar a senha + // Verificar se a senha está correta const isPasswordValid = await bcrypt.compare(password, user.password) if (!isPasswordValid) { return res.status(401).json({ message: 'Invalid credentials' }) } - const horaParaDias: string = `${24 * 356}h` // Gerar token JWT const token = jwt.sign( { id: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET || 'your_jwt_secret', - { expiresIn: horaParaDias } // 8544h == 356 dias + { expiresIn: '1d' } ) - res.status(200).json({ token }) + // Retornar o token e as informações do usuário + res.status(200).json({ + message: 'Login successful', + token, + user: { + id: user.id, + email: user.email, + role: user.role, + }, + }) } catch (error) { console.error(error) res.status(500).json({ message: 'Server error' }) } -} +} \ No newline at end of file diff --git a/src/controllers/createUnitInCompany.ts b/src/controllers/createUnitInCompany.ts new file mode 100644 index 0000000..b059de3 --- /dev/null +++ b/src/controllers/createUnitInCompany.ts @@ -0,0 +1,48 @@ +import { PrismaClient } from '@prisma/client' +import type { Request, Response } from 'express' + +const prisma = new PrismaClient() + +export const createUnitInCompany = async (req: Request, res: Response) => { + const { name } = req.body + + // Verificar se o usuário está autenticado + if (!req.user || !req.user.id) { + return res.status(401).json({ message: 'User not authenticated' }) + } + + const adminUserId = req.user.id // Pega o ID do usuário autenticado + + try { + // Verificar se o usuário autenticado é realmente um ADMIN + const adminUser = await prisma.user.findUnique({ + where: { id: adminUserId }, + include: { company: true }, // Incluir a empresa relacionada + }) + + if (!adminUser || adminUser.role !== 'ADMIN') { + return res.status(403).json({ message: 'Only administrators can create units.' }) + } + + // Verifica se o adminUser tem uma empresa associada + if (!adminUser.companyId) { + return res.status(400).json({ message: 'Admin user does not have a company associated.' }) + } + + // Criar a nova unidade e associá-la à empresa do administrador + const newUnit = await prisma.unit.create({ + data: { + name, + company: { connect: { id: adminUser.companyId } }, // Associa a unidade à empresa do administrador + }, + }) + + res.status(201).json({ + message: 'Unit created successfully', + unit: newUnit, + }) + } catch (error) { + console.error(error) + res.status(500).json({ message: 'Server error' }) + } +} diff --git a/src/controllers/createUserController.ts b/src/controllers/createUserController.ts new file mode 100644 index 0000000..b97b8f3 --- /dev/null +++ b/src/controllers/createUserController.ts @@ -0,0 +1,65 @@ +import { PrismaClient } from '@prisma/client' +import bcrypt from 'bcrypt' +import type { Request, Response } from 'express' + +const prisma = new PrismaClient() +const saltRounds = 10 + +export const createUserInCompany = async (req: Request, res: Response) => { + const { email, password, name, role } = req.body + + // Verifica se req.user existe antes de tentar acessá-lo + if (!req.user || !req.user.id) { + return res.status(401).json({ message: 'User not authenticated' }) + } + + const adminUserId = req.user.id // Pega o ID do usuário autenticado (administrador) + + try { + // Verificar se o usuário autenticado é realmente um ADMIN + const adminUser = await prisma.user.findUnique({ + where: { id: adminUserId }, + include: { company: true }, // Incluir a empresa relacionada + }) + + if (!adminUser || adminUser.role !== 'ADMIN') { + return res.status(403).json({ message: 'Only administrators can create users.' }) + } + + // Verifica se o adminUser tem uma empresa associada + if (!adminUser.companyId) { + return res.status(400).json({ message: 'Admin user does not have a company associated.' }) + } + + // Verificar se o email já existe + const existingUser = await prisma.user.findUnique({ + where: { email }, + }) + + if (existingUser) { + return res.status(400).json({ message: 'User already exists' }) + } + + // Criptografar a senha + const hashedPassword = await bcrypt.hash(password, saltRounds) + + // Criar o novo usuário e associá-lo à mesma empresa do administrador + const newUser = await prisma.user.create({ + data: { + email, + password: hashedPassword, + name, + role: role || 'USER', // Por padrão, novos usuários são do tipo 'USER' + company: { connect: { id: adminUser.companyId } }, // Associa o novo usuário à empresa do admin + }, + }) + + res.status(201).json({ + message: 'User created successfully', + user: newUser, + }) + } catch (error) { + console.error(error) + res.status(500).json({ message: 'Server error' }) + } +} diff --git a/src/routes/createUnitRoutes.ts b/src/routes/createUnitRoutes.ts new file mode 100644 index 0000000..f52f1c2 --- /dev/null +++ b/src/routes/createUnitRoutes.ts @@ -0,0 +1,11 @@ +import express from 'express' +import { createUnitInCompany } from '@/controllers/createUnitInCompany' +import authenticate from '../middleware/authenticate' +import authorize from '../middleware/authorize' + +const createUnitRouter = express.Router() + +// Rota protegida para criar unidades dentro da empresa do administrador +createUnitRouter.post('/create-unit', authenticate, authorize(['ADMIN']), createUnitInCompany) + +export default createUnitRouter diff --git a/src/routes/createUserRoutes.ts b/src/routes/createUserRoutes.ts new file mode 100644 index 0000000..0c21df9 --- /dev/null +++ b/src/routes/createUserRoutes.ts @@ -0,0 +1,11 @@ +import express from 'express' +import { createUserInCompany } from '@/controllers/createUserController' +import authenticate from '../middleware/authenticate' +import authorize from '../middleware/authorize' + +const createUserRouter = express.Router() + +// Rota protegida para criar usuários dentro da empresa do administrador +createUserRouter.post('/create-user', authenticate, authorize(['ADMIN']), createUserInCompany) + +export default createUserRouter diff --git a/src/services/companyService.ts b/src/services/companyService.ts index 53be722..8583fd2 100644 --- a/src/services/companyService.ts +++ b/src/services/companyService.ts @@ -5,7 +5,10 @@ const prisma = new PrismaClient() export const createCompany = async (companyData: Prisma.CompanyCreateInput) => { return prisma.company.create({ - data: companyData + data: { + name: companyData.name, + document: companyData.document, + } }) } @@ -27,7 +30,7 @@ export const updateCompany = async ( where: { id }, data: companyData, }) - } +} export const deleteCompany = async (id: number) => { return prisma.company.delete({ diff --git a/src/utils/validators.ts b/src/utils/validators.ts new file mode 100644 index 0000000..8987cb5 --- /dev/null +++ b/src/utils/validators.ts @@ -0,0 +1,9 @@ +export const validateCNPJ = (cnpj: string) => { + // Adicione aqui a lógica de validação de CNPJ + return true // Retorne true se for válido, false se não for +} + +export const validateCPF = (cpf: string) => { + // Adicione aqui a lógica de validação de CPF + return true // Retorne true se for válido, false se não for +}