Files
sgse-app/packages/backend/convex/tables/auth.ts

164 lines
5.3 KiB
TypeScript

import { defineTable } from 'convex/server';
import { v } from 'convex/values';
export const authTables = {
// Sistema de Autenticação e Controle de Acesso
usuarios: defineTable({
authId: v.string(),
nome: v.string(),
email: v.string(),
funcionarioId: v.optional(v.id('funcionarios')),
roleId: v.id('roles'),
ativo: v.boolean(),
primeiroAcesso: v.boolean(),
ultimoAcesso: v.optional(v.number()),
criadoEm: v.number(),
atualizadoEm: v.number(),
// Controle de Bloqueio e Segurança
bloqueado: v.optional(v.boolean()),
motivoBloqueio: v.optional(v.string()),
dataBloqueio: v.optional(v.number()),
tentativasLogin: v.optional(v.number()), // contador de tentativas falhas
ultimaTentativaLogin: v.optional(v.number()), // timestamp da última tentativa
// Campos de Chat e Perfil
fotoPerfil: v.optional(v.id('_storage')),
avatar: v.optional(v.string()), // URL do avatar gerado (ex: DiceBear)
statusMensagem: v.optional(v.string()), // max 100 chars
statusPresenca: v.optional(
v.union(
v.literal('online'),
v.literal('offline'),
v.literal('ausente'),
v.literal('externo'),
v.literal('em_reuniao')
)
),
ultimaAtividade: v.optional(v.number()), // timestamp
notificacoesAtivadas: v.optional(v.boolean()),
somNotificacao: v.optional(v.boolean()),
temaPreferido: v.optional(v.string()) // tema de aparência escolhido pelo usuário
})
.index('by_email', ['email'])
.index('by_role', ['roleId'])
.index('by_ativo', ['ativo'])
.index('by_status_presenca', ['statusPresenca'])
.index('by_bloqueado', ['bloqueado'])
.index('by_funcionarioId', ['funcionarioId'])
.index('authId', ['authId']),
roles: defineTable({
nome: v.string(), // "admin", "ti_master", "ti_usuario", "usuario_avancado", "usuario"
descricao: v.string(),
admin: v.optional(v.boolean()) // true = acesso total ao sistema, false/undefined = permissões via rolePermissoes
}).index('by_nome', ['nome']),
permissoes: defineTable({
nome: v.string(), // "funcionarios.criar", "simbolos.editar", etc.
descricao: v.string(),
recurso: v.string(), // "funcionarios", "simbolos", "usuarios", etc.
acao: v.string() // "criar", "ler", "editar", "excluir"
})
.index('by_recurso', ['recurso'])
.index('by_recurso_e_acao', ['recurso', 'acao'])
.index('by_nome', ['nome']),
rolePermissoes: defineTable({
roleId: v.id('roles'),
permissaoId: v.id('permissoes')
})
.index('by_role', ['roleId'])
.index('by_permissao', ['permissaoId']),
sessoes: defineTable({
usuarioId: v.id('usuarios'),
token: v.string(),
ipAddress: v.optional(v.string()),
userAgent: v.optional(v.string()),
criadoEm: v.number(),
expiraEm: v.number(),
ativo: v.boolean()
})
.index('by_usuario', ['usuarioId'])
.index('by_token', ['token'])
.index('by_ativo', ['ativo'])
.index('by_expiracao', ['expiraEm']),
logsAcesso: defineTable({
usuarioId: v.id('usuarios'),
tipo: v.union(
v.literal('login'),
v.literal('logout'),
v.literal('acesso_negado'),
v.literal('senha_alterada'),
v.literal('sessao_expirada')
),
ipAddress: v.optional(v.string()),
userAgent: v.optional(v.string()),
detalhes: v.optional(v.string()),
timestamp: v.number()
})
.index('by_usuario', ['usuarioId'])
.index('by_tipo', ['tipo'])
.index('by_timestamp', ['timestamp']),
// Histórico de Bloqueios
bloqueiosUsuarios: defineTable({
usuarioId: v.id('usuarios'),
motivo: v.string(),
bloqueadoPor: v.id('usuarios'), // ID do TI_MASTER que bloqueou
dataInicio: v.number(),
dataFim: v.optional(v.number()), // quando foi desbloqueado
desbloqueadoPor: v.optional(v.id('usuarios')),
ativo: v.boolean() // se é o bloqueio atual ativo
})
.index('by_usuario', ['usuarioId'])
.index('by_bloqueado_por', ['bloqueadoPor'])
.index('by_ativo', ['ativo'])
.index('by_data_inicio', ['dataInicio']),
configuracaoAcesso: defineTable({
chave: v.string(), // "sessao_duracao", "max_tentativas_login", etc.
valor: v.string(),
descricao: v.string()
}).index('by_chave', ['chave']),
// Logs de Login Detalhados
logsLogin: defineTable({
usuarioId: v.optional(v.id('usuarios')), // pode ser null se falha antes de identificar usuário
matriculaOuEmail: v.string(), // tentativa de login
sucesso: v.boolean(),
motivoFalha: v.optional(v.string()), // "senha_incorreta", "usuario_bloqueado", "usuario_inexistente"
// Informações de Rede
ipAddress: v.optional(v.string()),
ipPublico: v.optional(v.string()),
ipLocal: v.optional(v.string()),
userAgent: v.optional(v.string()),
device: v.optional(v.string()),
browser: v.optional(v.string()),
sistema: v.optional(v.string()),
// Informações de Localização (por IP)
latitude: v.optional(v.number()),
longitude: v.optional(v.number()),
endereco: v.optional(v.string()),
cidade: v.optional(v.string()),
estado: v.optional(v.string()),
pais: v.optional(v.string()),
// Informações de Localização (GPS do navegador)
latitudeGPS: v.optional(v.number()),
longitudeGPS: v.optional(v.number()),
precisaoGPS: v.optional(v.number()),
enderecoGPS: v.optional(v.string()),
cidadeGPS: v.optional(v.string()),
estadoGPS: v.optional(v.string()),
paisGPS: v.optional(v.string()),
timestamp: v.number()
})
.index('by_usuario', ['usuarioId'])
.index('by_sucesso', ['sucesso'])
.index('by_timestamp', ['timestamp'])
.index('by_ip', ['ipAddress'])
};