feat: Introduce structured table definitions in convex/tables for various entities and remove the todos example table.
This commit is contained in:
220
packages/backend/convex/tables/system.ts
Normal file
220
packages/backend/convex/tables/system.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
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(),
|
||||
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'])
|
||||
};
|
||||
Reference in New Issue
Block a user