156 lines
5.8 KiB
TypeScript
156 lines
5.8 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import bcrypt from 'bcryptjs';
|
|
import { db } from '../config/database';
|
|
import { generateTokens } from '../middleware/auth';
|
|
import { auditLog } from '../utils/audit';
|
|
|
|
export async function register(req: Request, res: Response) {
|
|
const { name, email, password, type, whatsapp, address, pj_data } = req.body;
|
|
|
|
// Validations (before transaction)
|
|
if (!name || !email || !password) {
|
|
return res.status(400).json({ error: 'Campos obrigatórios faltando (nome, email, senha)' });
|
|
}
|
|
|
|
const trx = await db.transaction();
|
|
try {
|
|
const existingUser = await trx('users').where({ email }).first();
|
|
if (existingUser) {
|
|
await trx.rollback();
|
|
return res.status(400).json({ error: 'E-mail já cadastrado' });
|
|
}
|
|
|
|
const password_hash = await bcrypt.hash(password, 10);
|
|
|
|
// Create User
|
|
const [userId] = await trx('users').insert({
|
|
name,
|
|
email,
|
|
password_hash,
|
|
whatsapp,
|
|
role: type === 'partner' ? 'partner_admin' : 'user',
|
|
state: address?.state || null,
|
|
// city: address?.city, // Column doesn't exist yet, ignoring to avoid crash
|
|
// neighborhood: address?.neighborhood, // Column doesn't exist yet
|
|
status: 'active', // Users are active by default (partners rely on partner status)
|
|
created_at: new Date(),
|
|
updated_at: new Date()
|
|
});
|
|
|
|
// Create Partner (PJ)
|
|
if (type === 'partner' && pj_data) {
|
|
// Check existing CNPJ
|
|
if (pj_data.cnpj) {
|
|
const existingPartner = await trx('partners').where({ cnpj: pj_data.cnpj }).first();
|
|
if (existingPartner) {
|
|
await trx.rollback();
|
|
return res.status(400).json({ error: 'CNPJ já cadastrado' });
|
|
}
|
|
}
|
|
|
|
const [partnerId] = await trx('partners').insert({
|
|
company_name: pj_data.fantasy_name,
|
|
slug: null, // Removed automatic random slug. User will choose via modal.
|
|
cnpj: pj_data.cnpj || null,
|
|
email, // Contact email
|
|
phone: whatsapp,
|
|
address_street: address?.street,
|
|
address_number: address?.number,
|
|
address_neighborhood: address?.neighborhood,
|
|
address_city: address?.city,
|
|
address_state: address?.state,
|
|
type: String(pj_data.category_id || 'Serviços'),
|
|
status: 'inactive', // 'pending' doesn't exist in enum ('active','inactive'), so using 'inactive' as pending approval
|
|
owner_user_id: userId,
|
|
created_at: new Date(),
|
|
updated_at: new Date(),
|
|
data_consent: 1 // Implicit consent
|
|
});
|
|
|
|
// Link user to partner
|
|
await trx('users').where({ id: userId }).update({ partner_id: partnerId });
|
|
}
|
|
|
|
await trx.commit();
|
|
|
|
// Auto-login response
|
|
const user = await db('users').where({ id: userId }).first();
|
|
const tokens = generateTokens(user);
|
|
const redirectPath = getRedirectPath(user);
|
|
|
|
// Log action (outside transaction)
|
|
await auditLog(user.id, 'REGISTER', 'user', user.id, `Novo cadastro: ${type}`, req.ip);
|
|
|
|
res.status(201).json({ user, redirectPath, ...tokens });
|
|
|
|
} catch (err: any) {
|
|
await trx.rollback();
|
|
console.error('Register Error:', err);
|
|
res.status(500).json({ error: 'Erro ao criar conta: ' + (err.message || 'Erro interno') });
|
|
}
|
|
}
|
|
|
|
export async function login(req: Request, res: Response) {
|
|
try {
|
|
const { email, password } = req.body;
|
|
const user = await db('users').where({ email }).first();
|
|
if (!user || user.deleted_at || user.status === 'banned') {
|
|
return res.status(401).json({ error: 'Credenciais inválidas ou conta desativada' });
|
|
}
|
|
if (!(await bcrypt.compare(password, user.password_hash))) {
|
|
return res.status(401).json({ error: 'Credenciais inválidas' });
|
|
}
|
|
const tokens = generateTokens(user);
|
|
await auditLog(user.id, 'LOGIN', 'user', user.id, 'Usuário logou no sistema', req.ip);
|
|
const redirectPath = getRedirectPath(user);
|
|
res.json({ user, redirectPath, ...tokens });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: 'Erro no servidor' });
|
|
}
|
|
}
|
|
|
|
function getRedirectPath(user: any) {
|
|
if (user.role === 'super_admin') return '/admin/dashboard';
|
|
if (user.role === 'partner' || user.role === 'partner_admin') {
|
|
return '/partner/dashboard';
|
|
}
|
|
return '/user/dashboard';
|
|
}
|
|
|
|
export async function profile(req: Request, res: Response) {
|
|
try {
|
|
const user = await db('users')
|
|
.select('users.*', 'partners.slug as partner_slug')
|
|
.leftJoin('partners', 'users.partner_id', 'partners.id')
|
|
.where('users.id', req.user!.id)
|
|
.first();
|
|
res.json(user);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Erro ao buscar perfil' });
|
|
}
|
|
}
|
|
|
|
export async function completeProfile(req: Request, res: Response) {
|
|
try {
|
|
const userId = req.user!.id;
|
|
const { whatsapp, state, city, neighborhood } = req.body || {};
|
|
|
|
if (!whatsapp || !state || !city || !neighborhood) {
|
|
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
|
|
}
|
|
|
|
await db('users').where({ id: userId }).update({
|
|
whatsapp,
|
|
state,
|
|
city,
|
|
neighborhood,
|
|
updated_at: new Date(),
|
|
});
|
|
|
|
const user = await db('users').where({ id: userId }).first();
|
|
res.json(user);
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: 'Erro ao completar cadastro' });
|
|
}
|
|
}
|