feat: Introduce structured table definitions in convex/tables for various entities and remove the todos example table.
This commit is contained in:
172
packages/backend/convex/tables/auth.ts
Normal file
172
packages/backend/convex/tables/auth.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
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)
|
||||
setor: v.optional(v.string()),
|
||||
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(),
|
||||
nivel: v.number(), // 0 = admin, 1 = ti_master, 2 = ti_usuario, 3+ = customizado
|
||||
setor: v.optional(v.string()), // "ti", "rh", "financeiro", etc.
|
||||
customizado: v.optional(v.boolean()), // se é um perfil customizado criado por TI_MASTER
|
||||
criadoPor: v.optional(v.id('usuarios')), // usuário TI_MASTER que criou este perfil
|
||||
editavel: v.optional(v.boolean()) // se pode ser editado (false para roles fixas)
|
||||
})
|
||||
.index('by_nome', ['nome'])
|
||||
.index('by_nivel', ['nivel'])
|
||||
.index('by_setor', ['setor'])
|
||||
.index('by_customizado', ['customizado']),
|
||||
|
||||
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'])
|
||||
};
|
||||
Reference in New Issue
Block a user