241 lines
8.4 KiB
TypeScript
241 lines
8.4 KiB
TypeScript
import { defineTable } from 'convex/server';
|
|
import { v } from 'convex/values';
|
|
import { ataqueCiberneticoTipo, severidadeSeguranca } from './security';
|
|
|
|
export const systemTables = {
|
|
// Logs de Atividades do Sistema
|
|
logsAtividades: defineTable({
|
|
usuarioId: v.id('usuarios'),
|
|
acao: v.string(),
|
|
recurso: v.string(),
|
|
recursoId: v.optional(v.string()),
|
|
detalhes: v.optional(v.string()),
|
|
timestamp: v.number()
|
|
})
|
|
.index('by_usuario', ['usuarioId'])
|
|
.index('by_acao', ['acao'])
|
|
.index('by_recurso', ['recurso'])
|
|
.index('by_timestamp', ['timestamp'])
|
|
.index('by_recurso_id', ['recurso', 'recursoId']),
|
|
|
|
// Configuração de Email/SMTP
|
|
configuracaoEmail: defineTable({
|
|
servidor: v.string(), // smtp.gmail.com
|
|
porta: v.number(), // 587, 465, etc.
|
|
usuario: v.string(),
|
|
senhaHash: v.string(), // senha criptografada reversível (AES-GCM) - necessário para descriptografar e usar no SMTP
|
|
emailRemetente: v.string(),
|
|
nomeRemetente: v.string(),
|
|
usarSSL: v.boolean(),
|
|
usarTLS: v.boolean(),
|
|
ativo: v.boolean(),
|
|
testadoEm: v.optional(v.number()),
|
|
configuradoPor: v.id('usuarios'),
|
|
atualizadoEm: v.number()
|
|
}).index('by_ativo', ['ativo']),
|
|
|
|
// Fila de Emails
|
|
notificacoesEmail: defineTable({
|
|
destinatario: v.string(), // email
|
|
destinatarioId: v.optional(v.id('usuarios')),
|
|
assunto: v.string(),
|
|
corpo: v.string(), // HTML ou texto
|
|
templateId: v.optional(v.id('templatesMensagens')),
|
|
status: v.union(
|
|
v.literal('pendente'),
|
|
v.literal('enviando'),
|
|
v.literal('enviado'),
|
|
v.literal('falha')
|
|
),
|
|
tentativas: v.number(),
|
|
ultimaTentativa: v.optional(v.number()),
|
|
erroDetalhes: v.optional(v.string()),
|
|
enviadoPor: v.id('usuarios'),
|
|
criadoEm: v.number(),
|
|
enviadoEm: v.optional(v.number()),
|
|
agendadaPara: v.optional(v.number()) // timestamp para agendamento
|
|
})
|
|
.index('by_status', ['status'])
|
|
.index('by_destinatario', ['destinatarioId'])
|
|
.index('by_enviado_por', ['enviadoPor'])
|
|
.index('by_criado_em', ['criadoEm'])
|
|
.index('by_agendamento', ['agendadaPara']),
|
|
|
|
// Rate Limiting de Emails
|
|
rateLimitEmails: defineTable({
|
|
remetenteId: v.id('usuarios'),
|
|
timestamp: v.number(),
|
|
contador: v.number(), // quantidade de emails enviados neste período
|
|
periodo: v.union(
|
|
v.literal('minuto'), // último minuto
|
|
v.literal('hora') // última hora
|
|
)
|
|
})
|
|
.index('by_remetente_periodo', ['remetenteId', 'periodo', 'timestamp'])
|
|
.index('by_timestamp', ['timestamp']),
|
|
|
|
// Tabelas de Monitoramento do Sistema
|
|
systemMetrics: defineTable({
|
|
timestamp: v.number(),
|
|
// Métricas de Sistema
|
|
cpuUsage: v.optional(v.number()),
|
|
memoryUsage: v.optional(v.number()),
|
|
networkLatency: v.optional(v.number()),
|
|
storageUsed: v.optional(v.number()),
|
|
// Métricas de Aplicação
|
|
usuariosOnline: v.optional(v.number()),
|
|
mensagensPorMinuto: v.optional(v.number()),
|
|
tempoRespostaMedio: v.optional(v.number()),
|
|
errosCount: v.optional(v.number())
|
|
}).index('by_timestamp', ['timestamp']),
|
|
|
|
alertConfigurations: defineTable({
|
|
metricName: v.string(),
|
|
threshold: v.number(),
|
|
operator: v.union(
|
|
v.literal('>'),
|
|
v.literal('<'),
|
|
v.literal('>='),
|
|
v.literal('<='),
|
|
v.literal('==')
|
|
),
|
|
enabled: v.boolean(),
|
|
notifyByEmail: v.boolean(),
|
|
notifyByChat: v.boolean(),
|
|
createdBy: v.id('usuarios'),
|
|
lastModified: v.number()
|
|
}).index('by_enabled', ['enabled']),
|
|
|
|
alertHistory: defineTable({
|
|
configId: v.id('alertConfigurations'),
|
|
metricName: v.string(),
|
|
metricValue: v.number(),
|
|
threshold: v.number(),
|
|
timestamp: v.number(),
|
|
status: v.union(v.literal('triggered'), v.literal('resolved')),
|
|
notificationsSent: v.object({
|
|
email: v.boolean(),
|
|
chat: v.boolean()
|
|
})
|
|
})
|
|
.index('by_timestamp', ['timestamp'])
|
|
.index('by_status', ['status'])
|
|
.index('by_config', ['configId', 'timestamp']),
|
|
|
|
rateLimitConfig: defineTable({
|
|
nome: v.string(),
|
|
tipo: v.union(
|
|
v.literal('ip'),
|
|
v.literal('usuario'),
|
|
v.literal('endpoint'),
|
|
v.literal('global')
|
|
),
|
|
identificador: v.optional(v.string()),
|
|
limite: v.number(),
|
|
janelaSegundos: v.number(),
|
|
estrategia: v.union(
|
|
v.literal('fixed_window'),
|
|
v.literal('sliding_window'),
|
|
v.literal('token_bucket')
|
|
),
|
|
acaoExcedido: v.union(v.literal('bloquear'), v.literal('throttle'), v.literal('alertar')),
|
|
bloqueioTemporarioSegundos: v.optional(v.number()),
|
|
ativo: v.boolean(),
|
|
prioridade: v.number(),
|
|
criadoPor: v.id('usuarios'),
|
|
atualizadoPor: v.optional(v.id('usuarios')),
|
|
criadoEm: v.number(),
|
|
atualizadoEm: v.number(),
|
|
notas: v.optional(v.string()),
|
|
tags: v.optional(v.array(v.string()))
|
|
})
|
|
.index('by_tipo_identificador', ['tipo', 'identificador'])
|
|
.index('by_ativo', ['ativo'])
|
|
.index('by_prioridade', ['prioridade']),
|
|
|
|
alertConfigs: defineTable({
|
|
nome: v.string(),
|
|
canais: v.object({
|
|
email: v.boolean(),
|
|
chat: v.boolean()
|
|
}),
|
|
emails: v.array(v.string()),
|
|
chatUsers: v.array(v.string()),
|
|
severidadeMin: severidadeSeguranca,
|
|
tiposAtaque: v.optional(v.array(ataqueCiberneticoTipo)),
|
|
reenvioMin: v.number(),
|
|
templateCodigo: v.optional(v.string()), // Template a ser usado para email/chat
|
|
criadoPor: v.id('usuarios'),
|
|
criadoEm: v.number(),
|
|
atualizadoEm: v.number()
|
|
}).index('by_criadoEm', ['criadoEm']),
|
|
|
|
// Configurações Gerais
|
|
config: defineTable({
|
|
comprasSetorId: v.optional(v.id('setores')),
|
|
criadoPor: v.id('usuarios'),
|
|
atualizadoEm: v.number()
|
|
}),
|
|
|
|
// Templates de Mensagens
|
|
templatesMensagens: defineTable({
|
|
codigo: v.string(), // "USUARIO_BLOQUEADO", "SENHA_RESETADA", etc.
|
|
nome: v.string(),
|
|
tipo: v.union(
|
|
v.literal('sistema'), // predefinido, não editável
|
|
v.literal('customizado') // criado por TI_MASTER
|
|
),
|
|
titulo: v.string(),
|
|
corpo: v.string(), // pode ter variáveis {{variavel}}
|
|
htmlCorpo: v.optional(v.string()), // versão HTML do corpo (com wrapper)
|
|
variaveis: v.optional(v.array(v.string())), // ["motivo", "senha", etc.]
|
|
categoria: v.optional(v.union(v.literal('email'), v.literal('chat'), v.literal('ambos'))), // categoria do template
|
|
tags: v.optional(v.array(v.string())), // tags para organização
|
|
criadoPor: v.optional(v.id('usuarios')),
|
|
criadoEm: v.number()
|
|
})
|
|
.index('by_codigo', ['codigo'])
|
|
.index('by_tipo', ['tipo'])
|
|
.index('by_criado_por', ['criadoPor'])
|
|
.index('by_categoria', ['categoria']),
|
|
|
|
// Configuração de Jitsi Meet
|
|
configuracaoJitsi: defineTable({
|
|
domain: v.string(), // Domínio do servidor Jitsi (ex: "localhost:8443" ou "meet.example.com")
|
|
appId: v.string(), // ID da aplicação Jitsi
|
|
roomPrefix: v.string(), // Prefixo para nomes de salas
|
|
useHttps: v.boolean(), // Usar HTTPS
|
|
acceptSelfSignedCert: v.optional(v.boolean()), // Aceitar certificados autoassinados (útil para desenvolvimento)
|
|
ativo: v.boolean(), // Configuração ativa
|
|
testadoEm: v.optional(v.number()), // Timestamp do último teste de conexão
|
|
configuradoEm: v.optional(v.number()), // Timestamp da última configuração do servidor Docker
|
|
configuradoNoServidor: v.optional(v.boolean()), // Indica se a configuração foi aplicada no servidor
|
|
configuradoNoServidorEm: v.optional(v.number()), // Timestamp de quando foi configurado no servidor
|
|
configuradoPor: v.id('usuarios'), // Usuário que configurou
|
|
atualizadoEm: v.number(), // Timestamp de atualização
|
|
jitsiConfigPath: v.optional(v.string()), // Caminho da configuração do Jitsi no servidor (ex: "~/.jitsi-meet-cfg")
|
|
sshUsername: v.optional(v.string()), // Usuário SSH para acesso ao servidor
|
|
sshPasswordHash: v.optional(v.string()), // Hash da senha SSH (criptografada)
|
|
sshPort: v.optional(v.number()) // Porta SSH (padrão: 22)
|
|
}).index('by_ativo', ['ativo']),
|
|
|
|
// Logs de Erros do Servidor (500, etc)
|
|
errosServidor: defineTable({
|
|
statusCode: v.number(), // Código HTTP do erro (500, 502, etc)
|
|
mensagem: v.string(), // Mensagem do erro
|
|
stack: v.optional(v.string()), // Stack trace do erro
|
|
url: v.optional(v.string()), // URL onde ocorreu o erro
|
|
method: v.optional(v.string()), // Método HTTP (GET, POST, etc)
|
|
ipAddress: v.optional(v.string()), // IP do cliente
|
|
userAgent: v.optional(v.string()), // User agent do navegador
|
|
usuarioId: v.optional(v.id('usuarios')), // Usuário autenticado (se houver)
|
|
notificado: v.boolean(), // Se a equipe técnica já foi notificada
|
|
notificadoEm: v.optional(v.number()), // Timestamp da notificação
|
|
criadoEm: v.number() // Timestamp do erro
|
|
})
|
|
.index('by_status_code', ['statusCode'])
|
|
.index('by_notificado', ['notificado'])
|
|
.index('by_criado_em', ['criadoEm'])
|
|
.index('by_usuario', ['usuarioId'])
|
|
};
|