feat: enhance cybersecurity features and add ticket management components
- Introduced new components for managing tickets, including TicketForm, TicketCard, and TicketTimeline, to streamline the ticketing process. - Added a new SlaChart component for visualizing SLA data. - Implemented a CybersecurityWizcard component for enhanced security monitoring and reporting. - Updated routing to replace the "Solicitar Acesso" page with "Abrir Chamado" for improved user navigation. - Integrated rate limiting functionality to enhance security measures. - Added a comprehensive test report for the cybersecurity system, detailing various attack simulations and their outcomes. - Included new scripts for security testing and environment setup to facilitate automated security assessments.
This commit is contained in:
@@ -7,6 +7,119 @@ export const simboloTipo = v.union(
|
||||
);
|
||||
export type SimboloTipo = Infer<typeof simboloTipo>;
|
||||
|
||||
export const ataqueCiberneticoTipo = v.union(
|
||||
v.literal("phishing"),
|
||||
v.literal("malware"),
|
||||
v.literal("ransomware"),
|
||||
v.literal("brute_force"),
|
||||
v.literal("credential_stuffing"),
|
||||
v.literal("sql_injection"),
|
||||
v.literal("xss"),
|
||||
v.literal("path_traversal"),
|
||||
v.literal("command_injection"),
|
||||
v.literal("nosql_injection"),
|
||||
v.literal("xxe"),
|
||||
v.literal("man_in_the_middle"),
|
||||
v.literal("ddos"),
|
||||
v.literal("engenharia_social"),
|
||||
v.literal("cve_exploit"),
|
||||
v.literal("apt"),
|
||||
v.literal("zero_day"),
|
||||
v.literal("supply_chain"),
|
||||
v.literal("fileless_malware"),
|
||||
v.literal("polymorphic_malware"),
|
||||
v.literal("ransomware_lateral"),
|
||||
v.literal("deepfake_phishing"),
|
||||
v.literal("adversarial_ai"),
|
||||
v.literal("side_channel"),
|
||||
v.literal("firmware_bootloader"),
|
||||
v.literal("bec"),
|
||||
v.literal("botnet"),
|
||||
v.literal("ot_ics"),
|
||||
v.literal("quantum_attack")
|
||||
);
|
||||
export type AtaqueCiberneticoTipo = Infer<typeof ataqueCiberneticoTipo>;
|
||||
|
||||
export const severidadeSeguranca = v.union(
|
||||
v.literal("informativo"),
|
||||
v.literal("baixo"),
|
||||
v.literal("moderado"),
|
||||
v.literal("alto"),
|
||||
v.literal("critico")
|
||||
);
|
||||
export type SeveridadeSeguranca = Infer<typeof severidadeSeguranca>;
|
||||
|
||||
export const statusEventoSeguranca = v.union(
|
||||
v.literal("detectado"),
|
||||
v.literal("investigando"),
|
||||
v.literal("contido"),
|
||||
v.literal("falso_positivo"),
|
||||
v.literal("escalado"),
|
||||
v.literal("resolvido")
|
||||
);
|
||||
export type StatusEventoSeguranca = Infer<typeof statusEventoSeguranca>;
|
||||
|
||||
export const sensorSegurancaTipo = v.union(
|
||||
v.literal("network"),
|
||||
v.literal("endpoint"),
|
||||
v.literal("application"),
|
||||
v.literal("gateway"),
|
||||
v.literal("ot"),
|
||||
v.literal("honeypot")
|
||||
);
|
||||
export type SensorSegurancaTipo = Infer<typeof sensorSegurancaTipo>;
|
||||
|
||||
export const sensorSegurancaStatus = v.union(
|
||||
v.literal("ativo"),
|
||||
v.literal("inativo"),
|
||||
v.literal("degradado"),
|
||||
v.literal("manutencao")
|
||||
);
|
||||
export type SensorSegurancaStatus = Infer<typeof sensorSegurancaStatus>;
|
||||
|
||||
export const threatIntelTipo = v.union(
|
||||
v.literal("open_source"),
|
||||
v.literal("commercial"),
|
||||
v.literal("internal"),
|
||||
v.literal("gov"),
|
||||
v.literal("research")
|
||||
);
|
||||
|
||||
export const threatIntelFormato = v.union(
|
||||
v.literal("json"),
|
||||
v.literal("stix"),
|
||||
v.literal("csv"),
|
||||
v.literal("text"),
|
||||
v.literal("custom")
|
||||
);
|
||||
|
||||
export const acaoIncidenteTipo = v.union(
|
||||
v.literal("block_ip"),
|
||||
v.literal("unblock_ip"),
|
||||
v.literal("block_port"),
|
||||
v.literal("liberar_porta"),
|
||||
v.literal("notificar"),
|
||||
v.literal("isolar_host"),
|
||||
v.literal("gerar_relatorio"),
|
||||
v.literal("criar_ticket"),
|
||||
v.literal("ajuste_regra"),
|
||||
v.literal("custom")
|
||||
);
|
||||
|
||||
export const acaoIncidenteStatus = v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("executando"),
|
||||
v.literal("concluido"),
|
||||
v.literal("falhou")
|
||||
);
|
||||
|
||||
export const reportStatus = v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("processando"),
|
||||
v.literal("concluido"),
|
||||
v.literal("falhou")
|
||||
);
|
||||
|
||||
export default defineSchema({
|
||||
todos: defineTable({
|
||||
text: v.string(),
|
||||
@@ -320,9 +433,12 @@ export default defineSchema({
|
||||
nome: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
gestorId: v.id("usuarios"),
|
||||
gestorSuperiorId: v.optional(v.id("usuarios")),
|
||||
ativo: v.boolean(),
|
||||
cor: v.optional(v.string()), // Cor para identificação visual
|
||||
}).index("by_gestor", ["gestorId"]),
|
||||
})
|
||||
.index("by_gestor", ["gestorId"])
|
||||
.index("by_gestor_superior", ["gestorSuperiorId"]),
|
||||
|
||||
timesMembros: defineTable({
|
||||
timeId: v.id("times"),
|
||||
@@ -688,7 +804,8 @@ export default defineSchema({
|
||||
v.literal("nova_mensagem"),
|
||||
v.literal("mencao"),
|
||||
v.literal("grupo_criado"),
|
||||
v.literal("adicionado_grupo")
|
||||
v.literal("adicionado_grupo"),
|
||||
v.literal("alerta_seguranca")
|
||||
),
|
||||
conversaId: v.optional(v.id("conversas")),
|
||||
mensagemId: v.optional(v.id("mensagens")),
|
||||
@@ -787,4 +904,446 @@ export default defineSchema({
|
||||
.index("by_timestamp", ["timestamp"])
|
||||
.index("by_status", ["status"])
|
||||
.index("by_config", ["configId", "timestamp"]),
|
||||
|
||||
tickets: defineTable({
|
||||
numero: v.string(),
|
||||
titulo: v.string(),
|
||||
descricao: v.string(),
|
||||
tipo: v.union(
|
||||
v.literal("reclamacao"),
|
||||
v.literal("elogio"),
|
||||
v.literal("sugestao"),
|
||||
v.literal("chamado")
|
||||
),
|
||||
categoria: v.optional(v.string()),
|
||||
status: v.union(
|
||||
v.literal("aberto"),
|
||||
v.literal("em_andamento"),
|
||||
v.literal("aguardando_usuario"),
|
||||
v.literal("resolvido"),
|
||||
v.literal("encerrado"),
|
||||
v.literal("cancelado")
|
||||
),
|
||||
prioridade: v.union(
|
||||
v.literal("baixa"),
|
||||
v.literal("media"),
|
||||
v.literal("alta"),
|
||||
v.literal("critica")
|
||||
),
|
||||
solicitanteId: v.id("usuarios"),
|
||||
solicitanteNome: v.string(),
|
||||
solicitanteEmail: v.string(),
|
||||
responsavelId: v.optional(v.id("usuarios")),
|
||||
setorResponsavel: v.optional(v.string()),
|
||||
slaConfigId: v.optional(v.id("slaConfigs")),
|
||||
conversaId: v.optional(v.id("conversas")),
|
||||
prazoResposta: v.optional(v.number()),
|
||||
prazoConclusao: v.optional(v.number()),
|
||||
prazoEncerramento: v.optional(v.number()),
|
||||
timeline: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
etapa: v.string(),
|
||||
status: v.union(
|
||||
v.literal("pendente"),
|
||||
v.literal("em_andamento"),
|
||||
v.literal("concluido"),
|
||||
v.literal("vencido")
|
||||
),
|
||||
prazo: v.optional(v.number()),
|
||||
concluidoEm: v.optional(v.number()),
|
||||
observacao: v.optional(v.string()),
|
||||
})
|
||||
)
|
||||
),
|
||||
alertasEmitidos: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
tipo: v.union(
|
||||
v.literal("resposta"),
|
||||
v.literal("conclusao"),
|
||||
v.literal("encerramento")
|
||||
),
|
||||
emitidoEm: v.number(),
|
||||
})
|
||||
)
|
||||
),
|
||||
anexos: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
arquivoId: v.id("_storage"),
|
||||
nome: v.optional(v.string()),
|
||||
tipo: v.optional(v.string()),
|
||||
tamanho: v.optional(v.number()),
|
||||
})
|
||||
)
|
||||
),
|
||||
tags: v.optional(v.array(v.string())),
|
||||
canalOrigem: v.optional(v.string()),
|
||||
ultimaInteracaoEm: v.number(),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.number(),
|
||||
})
|
||||
.index("by_numero", ["numero"])
|
||||
.index("by_status", ["status"])
|
||||
.index("by_solicitante", ["solicitanteId", "status"])
|
||||
.index("by_responsavel", ["responsavelId", "status"])
|
||||
.index("by_setor", ["setorResponsavel", "status"]),
|
||||
|
||||
ticketInteractions: defineTable({
|
||||
ticketId: v.id("tickets"),
|
||||
autorId: v.optional(v.id("usuarios")),
|
||||
origem: v.union(
|
||||
v.literal("usuario"),
|
||||
v.literal("ti"),
|
||||
v.literal("sistema")
|
||||
),
|
||||
tipo: v.union(
|
||||
v.literal("mensagem"),
|
||||
v.literal("status"),
|
||||
v.literal("anexo"),
|
||||
v.literal("alerta")
|
||||
),
|
||||
conteudo: v.string(),
|
||||
anexos: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
arquivoId: v.id("_storage"),
|
||||
nome: v.optional(v.string()),
|
||||
tipo: v.optional(v.string()),
|
||||
tamanho: v.optional(v.number()),
|
||||
})
|
||||
)
|
||||
),
|
||||
statusAnterior: v.optional(
|
||||
v.union(
|
||||
v.literal("aberto"),
|
||||
v.literal("em_andamento"),
|
||||
v.literal("aguardando_usuario"),
|
||||
v.literal("resolvido"),
|
||||
v.literal("encerrado"),
|
||||
v.literal("cancelado")
|
||||
)
|
||||
),
|
||||
statusNovo: v.optional(
|
||||
v.union(
|
||||
v.literal("aberto"),
|
||||
v.literal("em_andamento"),
|
||||
v.literal("aguardando_usuario"),
|
||||
v.literal("resolvido"),
|
||||
v.literal("encerrado"),
|
||||
v.literal("cancelado")
|
||||
)
|
||||
),
|
||||
visibilidade: v.union(
|
||||
v.literal("publico"),
|
||||
v.literal("interno")
|
||||
),
|
||||
criadoEm: v.number(),
|
||||
})
|
||||
.index("by_ticket", ["ticketId"])
|
||||
.index("by_ticket_type", ["ticketId", "tipo"])
|
||||
.index("by_autor", ["autorId"]),
|
||||
|
||||
slaConfigs: defineTable({
|
||||
nome: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
prioridade: v.optional(
|
||||
v.union(
|
||||
v.literal("baixa"),
|
||||
v.literal("media"),
|
||||
v.literal("alta"),
|
||||
v.literal("critica")
|
||||
)
|
||||
),
|
||||
tempoRespostaHoras: v.number(),
|
||||
tempoConclusaoHoras: v.number(),
|
||||
tempoEncerramentoHoras: v.optional(v.number()),
|
||||
alertaAntecedenciaHoras: v.number(),
|
||||
ativo: v.boolean(),
|
||||
criadoPor: v.id("usuarios"),
|
||||
atualizadoPor: v.optional(v.id("usuarios")),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.number(),
|
||||
})
|
||||
.index("by_ativo", ["ativo"])
|
||||
.index("by_prioridade", ["prioridade", "ativo"])
|
||||
.index("by_nome", ["nome"]),
|
||||
|
||||
ticketAssignments: defineTable({
|
||||
ticketId: v.id("tickets"),
|
||||
responsavelId: v.id("usuarios"),
|
||||
atribuidoPor: v.id("usuarios"),
|
||||
motivo: v.optional(v.string()),
|
||||
ativo: v.boolean(),
|
||||
criadoEm: v.number(),
|
||||
encerradoEm: v.optional(v.number()),
|
||||
})
|
||||
.index("by_ticket", ["ticketId", "ativo"])
|
||||
.index("by_responsavel", ["responsavelId", "ativo"]),
|
||||
|
||||
// Sistema de Segurança Cibernética
|
||||
networkSensors: defineTable({
|
||||
nome: v.string(),
|
||||
tipo: sensorSegurancaTipo,
|
||||
status: sensorSegurancaStatus,
|
||||
escopo: v.optional(v.string()),
|
||||
ipMonitorado: v.optional(v.string()),
|
||||
hostname: v.optional(v.string()),
|
||||
regioes: v.optional(v.array(v.string())),
|
||||
portasMonitoradas: v.optional(v.array(v.number())),
|
||||
protocolos: v.optional(v.array(v.string())),
|
||||
capacidades: v.optional(v.array(v.string())),
|
||||
ultimaSincronizacao: v.number(),
|
||||
ultimoHeartbeat: v.optional(v.number()),
|
||||
latenciaMs: v.optional(v.number()),
|
||||
errosConsecutivos: v.optional(v.number()),
|
||||
agenteVersao: v.optional(v.string()),
|
||||
notas: v.optional(v.string()),
|
||||
})
|
||||
.index("by_tipo", ["tipo"])
|
||||
.index("by_status", ["status"])
|
||||
.index("by_hostname", ["hostname"]),
|
||||
|
||||
ipReputation: defineTable({
|
||||
indicador: v.string(),
|
||||
categoria: v.union(
|
||||
v.literal("ip"),
|
||||
v.literal("dominio"),
|
||||
v.literal("hash"),
|
||||
v.literal("email")
|
||||
),
|
||||
reputacao: v.number(), // -100 (malicioso) até 100 (confiável)
|
||||
severidadeMax: severidadeSeguranca,
|
||||
whitelist: v.boolean(),
|
||||
blacklist: v.boolean(),
|
||||
ocorrencias: v.number(),
|
||||
primeiroRegistro: v.number(),
|
||||
ultimoRegistro: v.number(),
|
||||
bloqueadoAte: v.optional(v.number()),
|
||||
origem: v.optional(v.string()),
|
||||
comentarios: v.optional(v.string()),
|
||||
classificacoes: v.optional(v.array(v.string())),
|
||||
ultimaAcaoId: v.optional(v.id("incidentActions")),
|
||||
})
|
||||
.index("by_indicador", ["indicador"])
|
||||
.index("by_reputacao", ["reputacao"])
|
||||
.index("by_blacklist", ["blacklist"])
|
||||
.index("by_whitelist", ["whitelist"]),
|
||||
|
||||
portRules: defineTable({
|
||||
porta: v.number(),
|
||||
protocolo: v.union(
|
||||
v.literal("tcp"),
|
||||
v.literal("udp"),
|
||||
v.literal("icmp"),
|
||||
v.literal("quic"),
|
||||
v.literal("any")
|
||||
),
|
||||
acao: v.union(
|
||||
v.literal("permitir"),
|
||||
v.literal("bloquear"),
|
||||
v.literal("monitorar"),
|
||||
v.literal("rate_limit")
|
||||
),
|
||||
temporario: v.boolean(),
|
||||
severidadeMin: severidadeSeguranca,
|
||||
duracaoSegundos: v.optional(v.number()),
|
||||
expiraEm: v.optional(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())),
|
||||
listaReferencia: v.optional(v.id("ipReputation")),
|
||||
})
|
||||
.index("by_porta_protocolo", ["porta", "protocolo"])
|
||||
.index("by_acao", ["acao"])
|
||||
.index("by_expiracao", ["expiraEm"]),
|
||||
|
||||
threatIntelFeeds: defineTable({
|
||||
nomeFonte: v.string(),
|
||||
tipo: threatIntelTipo,
|
||||
formato: threatIntelFormato,
|
||||
url: v.optional(v.string()),
|
||||
ativo: v.boolean(),
|
||||
prioridade: v.union(
|
||||
v.literal("baixa"),
|
||||
v.literal("media"),
|
||||
v.literal("alta"),
|
||||
v.literal("critica")
|
||||
),
|
||||
ultimaSincronizacao: v.optional(v.number()),
|
||||
entradasProcessadas: v.optional(v.number()),
|
||||
errosConsecutivos: v.optional(v.number()),
|
||||
autenticacaoNecessaria: v.optional(v.boolean()),
|
||||
configuracao: v.optional(
|
||||
v.object({
|
||||
tokenId: v.optional(v.id("_storage")),
|
||||
escopo: v.optional(v.string()),
|
||||
})
|
||||
),
|
||||
criadoPor: v.id("usuarios"),
|
||||
atualizadoPor: v.optional(v.id("usuarios")),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.number(),
|
||||
})
|
||||
.index("by_tipo", ["tipo"])
|
||||
.index("by_ativo", ["ativo"])
|
||||
.index("by_prioridade", ["prioridade"]),
|
||||
|
||||
securityEvents: defineTable({
|
||||
referencia: v.string(),
|
||||
timestamp: v.number(),
|
||||
tipoAtaque: ataqueCiberneticoTipo,
|
||||
severidade: severidadeSeguranca,
|
||||
status: statusEventoSeguranca,
|
||||
descricao: v.string(),
|
||||
origemIp: v.optional(v.string()),
|
||||
origemRegiao: v.optional(v.string()),
|
||||
origemAsn: v.optional(v.string()),
|
||||
destinoIp: v.optional(v.string()),
|
||||
destinoPorta: v.optional(v.number()),
|
||||
protocolo: v.optional(v.string()),
|
||||
transporte: v.optional(v.string()),
|
||||
sensorId: v.optional(v.id("networkSensors")),
|
||||
detectadoPor: v.optional(v.string()),
|
||||
mitreTechnique: v.optional(v.string()),
|
||||
geolocalizacao: v.optional(
|
||||
v.object({
|
||||
pais: v.optional(v.string()),
|
||||
regiao: v.optional(v.string()),
|
||||
cidade: v.optional(v.string()),
|
||||
latitude: v.optional(v.number()),
|
||||
longitude: v.optional(v.number()),
|
||||
})
|
||||
),
|
||||
fingerprint: v.optional(
|
||||
v.object({
|
||||
userAgent: v.optional(v.string()),
|
||||
deviceId: v.optional(v.string()),
|
||||
ja3: v.optional(v.string()),
|
||||
tlsVersion: v.optional(v.string()),
|
||||
})
|
||||
),
|
||||
indicadores: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
tipo: v.string(),
|
||||
valor: v.string(),
|
||||
confianca: v.optional(v.number()),
|
||||
})
|
||||
)
|
||||
),
|
||||
metricas: v.optional(
|
||||
v.object({
|
||||
pps: v.optional(v.number()),
|
||||
bps: v.optional(v.number()),
|
||||
rpm: v.optional(v.number()),
|
||||
errosPorSegundo: v.optional(v.number()),
|
||||
hostsAfetados: v.optional(v.number()),
|
||||
})
|
||||
),
|
||||
correlacoes: v.optional(v.array(v.id("securityEvents"))),
|
||||
referenciasExternas: v.optional(v.array(v.string())),
|
||||
tags: v.optional(v.array(v.string())),
|
||||
criadoPor: v.optional(v.id("usuarios")),
|
||||
atualizadoEm: v.number(),
|
||||
})
|
||||
.index("by_referencia", ["referencia"])
|
||||
.index("by_timestamp", ["timestamp"])
|
||||
.index("by_tipo", ["tipoAtaque", "timestamp"])
|
||||
.index("by_severidade", ["severidade", "timestamp"])
|
||||
.index("by_status", ["status", "timestamp"]),
|
||||
|
||||
incidentActions: defineTable({
|
||||
eventoId: v.id("securityEvents"),
|
||||
tipo: acaoIncidenteTipo,
|
||||
origem: v.union(v.literal("automatico"), v.literal("manual")),
|
||||
status: acaoIncidenteStatus,
|
||||
executadoPor: v.optional(v.id("usuarios")),
|
||||
detalhes: v.optional(v.string()),
|
||||
resultado: v.optional(v.string()),
|
||||
relacionadoA: v.optional(v.id("ipReputation")),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.number(),
|
||||
})
|
||||
.index("by_evento", ["eventoId", "status"])
|
||||
.index("by_tipo", ["tipo", "status"]),
|
||||
|
||||
reportRequests: defineTable({
|
||||
solicitanteId: v.id("usuarios"),
|
||||
filtros: v.object({
|
||||
dataInicio: v.number(),
|
||||
dataFim: v.number(),
|
||||
severidades: v.optional(v.array(severidadeSeguranca)),
|
||||
tiposAtaque: v.optional(v.array(ataqueCiberneticoTipo)),
|
||||
incluirIndicadores: v.optional(v.boolean()),
|
||||
incluirMetricas: v.optional(v.boolean()),
|
||||
incluirAcoes: v.optional(v.boolean()),
|
||||
}),
|
||||
status: reportStatus,
|
||||
resultadoId: v.optional(v.id("_storage")),
|
||||
observacoes: v.optional(v.string()),
|
||||
criadoEm: v.number(),
|
||||
atualizadoEm: v.number(),
|
||||
concluidoEm: v.optional(v.number()),
|
||||
erro: v.optional(v.string()),
|
||||
})
|
||||
.index("by_status", ["status"])
|
||||
.index("by_solicitante", ["solicitanteId", "status"])
|
||||
.index("by_criado_em", ["criadoEm"]),
|
||||
|
||||
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"])
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user