fix: resolve super_admin privileges and tenant management issues

- Fixed real backend deletion for tenants

- Allowed super_admins to manage other super_admins in Global Users

- Filtered teams based on selected tenant in user creation

- Protected system tenant from deletion
This commit is contained in:
Cauê Faleiros
2026-03-04 11:36:47 -03:00
parent 75631909df
commit d5b57835a7
5 changed files with 187 additions and 17 deletions

View File

@@ -457,7 +457,14 @@ apiRouter.get('/teams', async (req, res) => {
try {
const { tenantId } = req.query;
const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id;
const [rows] = await pool.query('SELECT * FROM teams WHERE tenant_id = ?', [effectiveTenantId]);
let q = 'SELECT * FROM teams';
const params = [];
if (effectiveTenantId && effectiveTenantId !== 'all') {
q += ' WHERE tenant_id = ?';
params.push(effectiveTenantId);
}
const [rows] = await pool.query(q, params);
res.json(rows);
} catch (error) {
res.status(500).json({ error: error.message });
@@ -509,13 +516,49 @@ apiRouter.post('/tenants', requireRole(['super_admin']), async (req, res) => {
await connection.beginTransaction();
const tid = `tenant_${crypto.randomUUID().split('-')[0]}`;
await connection.query('INSERT INTO tenants (id, name, slug, admin_email, status) VALUES (?, ?, ?, ?, ?)', [tid, name, slug, admin_email, status || 'active']);
const uid = `u_${crypto.randomUUID().split('-')[0]}`;
await connection.query('INSERT INTO users (id, tenant_id, name, email, role) VALUES (?, ?, ?, ?, ?)', [uid, tid, 'Admin', admin_email, 'admin']);
// Check if user already exists
const [existingUser] = await connection.query('SELECT id FROM users WHERE email = ?', [admin_email]);
if (existingUser.length === 0) {
const uid = `u_${crypto.randomUUID().split('-')[0]}`;
const placeholderHash = 'pending_setup';
await connection.query('INSERT INTO users (id, tenant_id, name, email, password_hash, role) VALUES (?, ?, ?, ?, ?, ?)', [uid, tid, 'Admin', admin_email, placeholderHash, 'admin']);
const token = crypto.randomBytes(32).toString('hex');
await connection.query('INSERT INTO password_resets (email, token, expires_at) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR))', [admin_email, token]);
const setupLink = `${process.env.APP_URL || 'http://localhost:3001'}/#/reset-password?token=${token}`;
await transporter.sendMail({
from: `"Fasto" <${process.env.MAIL_FROM || 'nao-responda@blyzer.com.br'}>`,
to: admin_email,
subject: 'Bem-vindo ao Fasto - Crie sua senha',
html: `<p>Você foi convidado para ser Admin. <a href="${setupLink}">Crie sua senha aqui</a>.</p>`
}).catch(err => console.error("Email failed:", err));
}
await connection.commit();
res.status(201).json({ id: tid });
} catch (error) { await connection.rollback(); res.status(500).json({ error: error.message }); } finally { connection.release(); }
});
apiRouter.put('/tenants/:id', requireRole(['super_admin']), async (req, res) => {
const { name, slug, admin_email, status } = req.body;
try {
await pool.query(
'UPDATE tenants SET name = ?, slug = ?, admin_email = ?, status = ? WHERE id = ?',
[name, slug || null, admin_email, status, req.params.id]
);
res.json({ message: 'Tenant updated successfully.' });
} catch (error) { res.status(500).json({ error: error.message }); }
});
apiRouter.delete('/tenants/:id', requireRole(['super_admin']), async (req, res) => {
try {
await pool.query('DELETE FROM tenants WHERE id = ?', [req.params.id]);
res.json({ message: 'Tenant deleted successfully.' });
} catch (error) { res.status(500).json({ error: error.message }); }
});
// Mount the API Router
app.use('/api', apiRouter);
@@ -529,6 +572,55 @@ if (process.env.NODE_ENV === 'production') {
});
}
app.listen(PORT, () => {
// Auto-provision Super Admin
const provisionSuperAdmin = async () => {
const email = 'suporte@blyzer.com.br';
try {
// Ensure system tenant exists
await pool.query('INSERT IGNORE INTO tenants (id, name, slug, admin_email, status) VALUES (?, ?, ?, ?, ?)', ['system', 'System Admin', 'system', email, 'active']);
const [existing] = await pool.query('SELECT id FROM users WHERE email = ?', [email]);
if (existing.length === 0) {
console.log('Provisioning default super_admin...');
const uid = `u_${crypto.randomUUID().split('-')[0]}`;
const placeholderHash = 'pending_setup';
await pool.query(
'INSERT INTO users (id, tenant_id, name, email, password_hash, role, status) VALUES (?, ?, ?, ?, ?, ?, ?)',
[uid, 'system', 'Blyzer Suporte', email, placeholderHash, 'super_admin', 'active']
);
const token = crypto.randomBytes(32).toString('hex');
await pool.query(
'INSERT INTO password_resets (email, token, expires_at) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR))',
[email, token]
);
const setupLink = `${process.env.APP_URL || 'http://localhost:3001'}/#/reset-password?token=${token}`;
console.log(`\n\n=== SUPER ADMIN SETUP LINK ===\n${setupLink}\n==============================\n\n`);
await transporter.sendMail({
from: `"Fasto" <${process.env.MAIL_FROM || 'nao-responda@blyzer.com.br'}>`,
to: email,
subject: 'Conta Super Admin Criada - Fasto',
html: `
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #222; border-radius: 12px; background: #0a0a0a; color: #ededed;">
<h2 style="color: #facc15;">Conta Super Admin Gerada</h2>
<p>Sua conta de suporte (super_admin) foi criada no Fasto.</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${setupLink}" style="background-color: #facc15; color: #09090b; padding: 12px 24px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">Definir Senha do Super Admin</a>
</div>
<p style="font-size: 12px; color: #888;">Este link expira em 24 horas.</p>
</div>
`
}).catch(err => console.error("Failed to send super_admin email:", err));
}
} catch (error) {
console.error('Failed to provision super_admin:', error);
}
};
app.listen(PORT, async () => {
await provisionSuperAdmin();
console.log(`🚀 Servidor Backend rodando em http://localhost:${PORT}`);
});