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']) };