feat: inicializar repositorio oficial do clube67.com
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
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' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user