diff --git a/backend/index.js b/backend/index.js index 2469f33..bf90683 100644 --- a/backend/index.js +++ b/backend/index.js @@ -217,6 +217,30 @@ apiRouter.post('/auth/login', async (req, res) => { } }); +// God Mode (Impersonate Tenant) +apiRouter.post('/impersonate/:tenantId', requireRole(['super_admin']), async (req, res) => { + try { + // Buscar o primeiro admin (ou qualquer usuário) do tenant para assumir a identidade + const [users] = await pool.query("SELECT * FROM users WHERE tenant_id = ? AND role = 'admin' LIMIT 1", [req.params.tenantId]); + + if (users.length === 0) { + return res.status(404).json({ error: 'Nenhum administrador encontrado nesta organização para assumir a identidade.' }); + } + + const user = users[0]; + + if (user.status !== 'active') { + return res.status(403).json({ error: 'A conta do admin desta organização está inativa.' }); + } + + // Gerar um token JWT como se fôssemos o admin do tenant + const token = jwt.sign({ id: user.id, tenant_id: user.tenant_id, role: user.role, team_id: user.team_id, slug: user.slug }, JWT_SECRET, { expiresIn: '2h' }); + res.json({ token, user: { id: user.id, name: user.name, email: user.email, role: user.role, tenant_id: user.tenant_id, team_id: user.team_id, slug: user.slug } }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + // Forgot Password apiRouter.post('/auth/forgot-password', async (req, res) => { const { email } = req.body; diff --git a/pages/SuperAdmin.tsx b/pages/SuperAdmin.tsx index a7cbe14..0b84408 100644 --- a/pages/SuperAdmin.tsx +++ b/pages/SuperAdmin.tsx @@ -1,13 +1,16 @@ import React, { useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; import { Building2, Users, MessageSquare, Plus, Search, - Edit, Trash2, ChevronDown, ChevronUp, ChevronsUpDown, X, CheckCircle2, Loader2 -} from 'lucide-react';import { getTenants, createTenant, deleteTenant, updateTenant } from '../services/dataService'; + Edit, Trash2, ChevronDown, ChevronUp, ChevronsUpDown, X, CheckCircle2, Loader2, LogIn +} from 'lucide-react'; +import { getTenants, createTenant, deleteTenant, updateTenant, impersonateTenant } from '../services/dataService'; import { Tenant } from '../types'; import { DateRangePicker } from '../components/DateRangePicker'; import { KPICard } from '../components/KPICard'; export const SuperAdmin: React.FC = () => { + const navigate = useNavigate(); const [dateRange, setDateRange] = useState({ start: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), end: new Date() @@ -90,6 +93,20 @@ export const SuperAdmin: React.FC = () => { } }; + const handleImpersonate = async (tenantId: string) => { + try { + if (tenantId === 'system') { + alert('Você já está na organização do sistema.'); + return; + } + await impersonateTenant(tenantId); + // Force a full reload to clear any cached context/state in the React app + window.location.href = '/'; + } catch (err: any) { + alert(err.message || 'Erro ao tentar entrar na organização.'); + } + }; + const [successMessage, setSuccessMessage] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [isSaving, setIsSaving] = useState(false); @@ -232,8 +249,13 @@ export const SuperAdmin: React.FC = () => {