Files
mercado.clube67.com/backend/controllers/superAdminController.js
T

170 lines
6.4 KiB
JavaScript

'use strict'
const bcrypt = require('bcryptjs')
const { queryOne, query, execute } = require('../database/postgres')
const { signSuperToken } = require('../middleware/superAuth')
// ── Auth ──────────────────────────────────────────────────────────────────────
exports.login = async (req, res) => {
try {
const { email, password } = req.body
if (!email || !password)
return res.status(400).json({ error: 'Email e senha obrigatórios' })
const admin = await queryOne('SELECT * FROM super_admins WHERE email=$1', [email.toLowerCase()])
if (!admin) return res.status(401).json({ error: 'Credenciais inválidas' })
const ok = await bcrypt.compare(password, admin.password_hash)
if (!ok) return res.status(401).json({ error: 'Credenciais inválidas' })
const token = signSuperToken(admin)
res.cookie('super_token', token, {
httpOnly: true,
secure: process.env.COOKIE_SECURE === 'true',
sameSite: 'lax',
maxAge: 8 * 60 * 60 * 1000,
})
res.json({ ok: true, name: admin.name, email: admin.email })
} catch (err) {
console.error('[superAdmin] login:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}
exports.logout = (req, res) => {
res.clearCookie('super_token')
res.json({ ok: true })
}
exports.me = (req, res) => {
res.json({ id: req.superAdmin.id, name: req.superAdmin.name, email: req.superAdmin.email })
}
// ── Tenants ───────────────────────────────────────────────────────────────────
exports.listTenants = async (req, res) => {
try {
const tenants = await query(`
SELECT t.*,
tc.primary_color, tc.secondary_color, tc.logo_url,
tc.contact_email, tc.contact_phone, tc.app_url
FROM tenants t
LEFT JOIN tenant_config tc ON tc.tenant_id = t.id
ORDER BY t.id
`)
res.json(tenants ?? [])
} catch (err) {
console.error('[superAdmin] listTenants:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}
exports.createTenant = async (req, res) => {
try {
const { slug, name, domain, primary_color, secondary_color,
logo_url, contact_email, contact_phone, app_url } = req.body
if (!slug || !name) return res.status(400).json({ error: 'slug e name obrigatórios' })
const result = await execute(
`INSERT INTO tenants (slug, name, domain, active) VALUES ($1,$2,$3,TRUE) RETURNING id`,
[slug.toLowerCase(), name, domain || null]
)
const tenantId = result.rows[0].id
await execute(
`INSERT INTO tenant_config
(tenant_id, primary_color, secondary_color, logo_url, contact_email, contact_phone, app_url)
VALUES ($1,$2,$3,$4,$5,$6,$7)`,
[tenantId, primary_color || '#f59e0b', secondary_color || '#10b981',
logo_url || null, contact_email || null, contact_phone || null, app_url || null]
)
res.status(201).json({ id: tenantId })
} catch (err) {
if (err.code === '23505') return res.status(409).json({ error: 'Slug já existe' })
console.error('[superAdmin] createTenant:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}
exports.updateTenant = async (req, res) => {
try {
const { id } = req.params
const { name, domain, active, primary_color, secondary_color,
logo_url, contact_email, contact_phone, app_url } = req.body
await execute(
`UPDATE tenants SET name=$1, domain=$2, active=$3 WHERE id=$4`,
[name, domain || null, active !== false, id]
)
await execute(
`UPDATE tenant_config
SET primary_color=$1, secondary_color=$2, logo_url=$3,
contact_email=$4, contact_phone=$5, app_url=$6
WHERE tenant_id=$7`,
[primary_color, secondary_color, logo_url || null,
contact_email || null, contact_phone || null, app_url || null, id]
)
res.json({ ok: true })
} catch (err) {
console.error('[superAdmin] updateTenant:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}
exports.deleteTenant = async (req, res) => {
try {
await execute('UPDATE tenants SET active=FALSE WHERE id=$1', [req.params.id])
res.json({ ok: true })
} catch (err) {
res.status(500).json({ error: 'Erro interno' })
}
}
exports.getTenantStats = async (req, res) => {
try {
const { id } = req.params
const tid = parseInt(id)
const [users, orders, tickets, clubMembers] = await Promise.all([
queryOne(`SELECT COUNT(*) AS cnt FROM users WHERE tenant_id=$1`, [tid]),
queryOne(`SELECT COUNT(*) AS cnt, COALESCE(SUM(total_amount),0) AS revenue
FROM orders WHERE tenant_id=$1 AND status='paid'`, [tid]),
queryOne(`SELECT COUNT(*) AS cnt FROM tickets WHERE tenant_id=$1 AND status='sold'`, [tid]),
queryOne(`SELECT COUNT(*) AS cnt FROM club_members WHERE tenant_id=$1 AND status='active'`, [tid]),
])
res.json({
users: Number(users?.cnt ?? 0),
orders: Number(orders?.cnt ?? 0),
revenue: Number(orders?.revenue ?? 0),
tickets_sold: Number(tickets?.cnt ?? 0),
club_members: Number(clubMembers?.cnt ?? 0),
})
} catch (err) {
console.error('[superAdmin] getTenantStats:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}
// ── Tenant Admins ─────────────────────────────────────────────────────────────
exports.createTenantAdmin = async (req, res) => {
try {
const { id: tenantId } = req.params
const { username, password, name } = req.body
if (!username || !password) return res.status(400).json({ error: 'username e password obrigatórios' })
const hash = await bcrypt.hash(password, 10)
const result = await execute(
`INSERT INTO admins (username, password_hash, name, tenant_id) VALUES ($1,$2,$3,$4) RETURNING id`,
[username, hash, name || username, tenantId]
)
res.status(201).json({ id: result.rows[0].id })
} catch (err) {
if (err.code === '23505') return res.status(409).json({ error: 'Username já existe' })
console.error('[superAdmin] createTenantAdmin:', err.message)
res.status(500).json({ error: 'Erro interno' })
}
}