164 lines
5.3 KiB
TypeScript
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'])
|
|
};
|