Initial commit: LexMind - Plataforma Jurídica Inteligente

This commit is contained in:
bigtux
2026-02-10 15:46:26 -03:00
commit 08bd4f039d
108 changed files with 75782 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
#!/usr/bin/env npx ts-node
/**
* Script de Busca Diária de Publicações
*
* Executa busca de publicações em todos os processos ativos de todos os usuários.
* Pode ser executado via cron ou manualmente.
*
* Uso: npx ts-node scripts/buscar-publicacoes.ts
* Ou: node dist/scripts/buscar-publicacoes.js (se compilado)
*/
import { PrismaClient } from '@prisma/client'
import { buscarPublicacoesReais, buscarPublicacoesEmLote, PublicacaoEncontrada } from '../src/lib/diarios-service'
const prisma = new PrismaClient()
interface BuscaStats {
totalProcessos: number
processosComNovas: number
totalPublicacoes: number
erros: string[]
inicioEm: Date
fimEm?: Date
}
async function buscarPublicacoesDiarias(): Promise<BuscaStats> {
const stats: BuscaStats = {
totalProcessos: 0,
processosComNovas: 0,
totalPublicacoes: 0,
erros: [],
inicioEm: new Date(),
}
console.log('='.repeat(60))
console.log(`[${new Date().toISOString()}] Iniciando busca diária de publicações`)
console.log('='.repeat(60))
try {
// Busca todos os processos ativos
const processos = await prisma.processoMonitorado.findMany({
where: { status: 'ATIVO' },
select: {
id: true,
numeroProcesso: true,
tribunal: true,
userId: true,
user: {
select: { email: true }
}
},
})
stats.totalProcessos = processos.length
console.log(`\n📋 Total de processos ativos: ${processos.length}`)
if (processos.length === 0) {
console.log('Nenhum processo ativo encontrado.')
return stats
}
// Busca publicações em lote com rate limiting (500ms entre requisições)
console.log('\n🔍 Buscando publicações nos diários oficiais...\n')
const resultados = await buscarPublicacoesEmLote(
processos.map(p => ({
id: p.id,
numeroProcesso: p.numeroProcesso,
tribunal: p.tribunal,
})),
500 // delay entre requisições
)
// Processa resultados
for (const processo of processos) {
const resultado = resultados.get(processo.id)
if (!resultado) {
stats.erros.push(`${processo.numeroProcesso}: Sem resultado`)
continue
}
if (!resultado.sucesso) {
stats.erros.push(`${processo.numeroProcesso}: ${resultado.erro}`)
continue
}
let novasDoProcesso = 0
for (const pub of resultado.publicacoes) {
// Verifica duplicata
const existing = await prisma.publicacao.findFirst({
where: {
processoId: processo.id,
dataPublicacao: {
gte: new Date(pub.dataPublicacao.toDateString()),
lt: new Date(new Date(pub.dataPublicacao).setDate(pub.dataPublicacao.getDate() + 1)),
},
tipo: pub.tipo,
},
})
if (!existing) {
await prisma.publicacao.create({
data: {
processoId: processo.id,
dataPublicacao: pub.dataPublicacao,
diario: pub.diario,
conteudo: pub.conteudo,
tipo: pub.tipo,
prazoCalculado: pub.prazoCalculado,
prazoTipo: pub.prazoTipo,
visualizado: false,
},
})
novasDoProcesso++
stats.totalPublicacoes++
}
}
if (novasDoProcesso > 0) {
stats.processosComNovas++
console.log(`${processo.numeroProcesso}: ${novasDoProcesso} nova(s) publicação(ões)`)
} else {
console.log(` ${processo.numeroProcesso}: sem novas publicações`)
}
}
} catch (error) {
const msg = error instanceof Error ? error.message : 'Erro desconhecido'
stats.erros.push(`Erro geral: ${msg}`)
console.error('\n❌ Erro durante execução:', error)
} finally {
await prisma.$disconnect()
}
stats.fimEm = new Date()
// Resumo final
console.log('\n' + '='.repeat(60))
console.log('📊 RESUMO DA BUSCA')
console.log('='.repeat(60))
console.log(`Processos verificados: ${stats.totalProcessos}`)
console.log(`Processos com novas publicações: ${stats.processosComNovas}`)
console.log(`Total de novas publicações: ${stats.totalPublicacoes}`)
console.log(`Erros: ${stats.erros.length}`)
if (stats.erros.length > 0) {
console.log('\n⚠ Erros encontrados:')
stats.erros.forEach(e => console.log(` - ${e}`))
}
const duracao = ((stats.fimEm.getTime() - stats.inicioEm.getTime()) / 1000).toFixed(1)
console.log(`\n⏱ Tempo de execução: ${duracao}s`)
console.log(`[${stats.fimEm.toISOString()}] Busca finalizada\n`)
return stats
}
// Executar
buscarPublicacoesDiarias()
.then(stats => {
process.exit(stats.erros.length > 0 ? 1 : 0)
})
.catch(error => {
console.error('Erro fatal:', error)
process.exit(1)
})

36
scripts/setup-stripe.ts Normal file
View File

@@ -0,0 +1,36 @@
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2025-04-30.basil' as any,
})
const plans = [
{ name: 'Teste', amount: 1, interval: 'month' as const },
{ name: 'Starter', amount: 9700, interval: 'month' as const },
{ name: 'Pro', amount: 19700, interval: 'month' as const },
{ name: 'Enterprise', amount: 49700, interval: 'month' as const },
]
async function main() {
console.log('Creating Stripe products and prices...\n')
for (const plan of plans) {
const product = await stripe.products.create({
name: `LexMind ${plan.name}`,
description: `Plano ${plan.name} - LexMind`,
})
const price = await stripe.prices.create({
product: product.id,
unit_amount: plan.amount,
currency: 'brl',
recurring: { interval: plan.interval },
})
console.log(`${plan.name}: product=${product.id} price=${price.id} (R$${(plan.amount / 100).toFixed(2)}/month)`)
}
console.log('\nDone! Copy the price IDs above into your code.')
}
main().catch(console.error)

102
scripts/testar-datajud.ts Normal file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env npx ts-node
/**
* Script de Teste da API DataJud
* Testa a conexão e busca um processo de exemplo
*/
// API Key pública do DataJud (CNJ)
const DATAJUD_API_KEY = 'cDZHYzlZa0JadVREZDJCendQbXY6SkJlTzNjLV9TRENyQk1RdnFKZGRQdw=='
const DATAJUD_BASE_URL = 'https://api-publica.datajud.cnj.jus.br'
async function testarDataJud() {
console.log('='.repeat(60))
console.log('🧪 Testando conexão com API DataJud (CNJ)')
console.log('='.repeat(60))
// Teste 1: Buscar um processo aleatório
console.log('\n1. Buscando processo de exemplo no TJSP...')
const url = `${DATAJUD_BASE_URL}/api_publica_tjsp/_search`
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `APIKey ${DATAJUD_API_KEY}`,
},
body: JSON.stringify({
size: 1,
query: {
bool: {
must: [
{ exists: { field: 'movimentos' } }
],
filter: [
{ range: { '@timestamp': { gte: 'now-7d' } } }
]
}
}
}),
})
if (!response.ok) {
console.log(`❌ Erro HTTP: ${response.status}`)
const text = await response.text()
console.log(text.substring(0, 500))
return
}
const data = await response.json()
console.log(`✅ Resposta OK - ${data.hits?.total?.value || 0} processos no índice`)
if (data.hits?.hits?.length > 0) {
const processo = data.hits.hits[0]._source
console.log(`\n📄 Processo encontrado:`)
console.log(` Número: ${processo.numeroProcesso}`)
console.log(` Classe: ${processo.classe?.nome || 'N/A'}`)
console.log(` Tribunal: ${processo.tribunal}`)
console.log(` Movimentos: ${processo.movimentos?.length || 0}`)
// Mostrar últimos movimentos
const ultimos = (processo.movimentos || []).slice(-5)
console.log('\n Últimos movimentos:')
ultimos.forEach((m: any) => {
console.log(` - ${m.dataHora?.substring(0, 10)} | ${m.nome} (código: ${m.codigo})`)
})
}
// Teste 2: Buscar processo específico (se existir)
console.log('\n\n2. Testando busca por número específico...')
const response2 = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `APIKey ${DATAJUD_API_KEY}`,
},
body: JSON.stringify({
size: 1,
query: {
match: {
numeroProcesso: '10000000020248260100' // Exemplo fictício
}
}
}),
})
const data2 = await response2.json()
const found = data2.hits?.hits?.length > 0
console.log(` Processo de teste: ${found ? '✅ Encontrado' : ' Não encontrado (esperado)'}`)
console.log('\n' + '='.repeat(60))
console.log('✅ API DataJud funcionando corretamente!')
console.log('='.repeat(60))
} catch (error) {
console.error('❌ Erro ao conectar:', error)
}
}
testarDataJud()

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env npx ts-node
/**
* Teste de Integração Completa
* Testa buscarPublicacoesReais com um número de processo real
*/
import { buscarPublicacoesReais, buscarDataJud } from '../src/lib/diarios-service'
async function testarIntegracao() {
console.log('='.repeat(60))
console.log('🧪 Teste de Integração - Busca de Publicações')
console.log('='.repeat(60))
// Processo de teste (usando um real do TJSP que sabemos existir)
const processoTeste = {
id: 'teste-001',
numeroProcesso: '1000044-50.2025.8.26.0220',
tribunal: 'TJSP',
}
console.log(`\n📋 Processo de teste:`)
console.log(` Número: ${processoTeste.numeroProcesso}`)
console.log(` Tribunal: ${processoTeste.tribunal}`)
console.log('\n🔍 Buscando publicações...\n')
try {
const resultado = await buscarPublicacoesReais(processoTeste, 365) // Últimos 365 dias
console.log(`✅ Busca concluída!`)
console.log(` Sucesso: ${resultado.sucesso}`)
console.log(` Fonte: ${resultado.fonte}`)
console.log(` Publicações encontradas: ${resultado.publicacoes.length}`)
if (resultado.erro) {
console.log(` Erro: ${resultado.erro}`)
}
if (resultado.publicacoes.length > 0) {
console.log('\n📰 Publicações encontradas:')
resultado.publicacoes.slice(0, 10).forEach((pub, i) => {
console.log(`\n ${i + 1}. ${pub.tipo}`)
console.log(` Data: ${pub.dataPublicacao.toISOString().split('T')[0]}`)
console.log(` Prazo: ${pub.prazoCalculado?.toISOString().split('T')[0]} (${pub.prazoTipo})`)
console.log(` Conteúdo: ${pub.conteudo.substring(0, 100)}...`)
})
}
// Testar também busca direta no DataJud
console.log('\n\n🔬 Testando busca direta DataJud (processo diferente)...')
const resultado2 = await buscarDataJud('0020077-82.2022.8.26.0576', 'TJSP')
console.log(` Publicações: ${resultado2.publicacoes.length}`)
} catch (error) {
console.error('❌ Erro:', error)
}
console.log('\n' + '='.repeat(60))
console.log('✅ Teste de integração finalizado')
console.log('='.repeat(60))
}
testarIntegracao()

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env npx ts-node
/**
* Teste Standalone da API DataJud
* Não depende de imports externos
*/
const DATAJUD_API_KEY = 'cDZHYzlZa0JadVREZDJCendQbXY6SkJlTzNjLV9TRENyQk1RdnFKZGRQdw=='
interface Movimento {
codigo: number
nome: string
dataHora: string
orgaoJulgador?: { nome: string }
complementosTabelados?: Array<{ nome: string }>
}
async function buscarDataJud(numeroProcesso: string, tribunal: string) {
const endpoint = `api_publica_${tribunal.toLowerCase()}`
const url = `https://api-publica.datajud.cnj.jus.br/${endpoint}/_search`
const numeroLimpo = numeroProcesso.replace(/\D/g, '')
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `APIKey ${DATAJUD_API_KEY}`,
},
body: JSON.stringify({
size: 1,
query: { match: { numeroProcesso: numeroLimpo } },
}),
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`)
}
return await response.json()
}
async function main() {
console.log('='.repeat(60))
console.log('🧪 Teste Standalone - API DataJud')
console.log('='.repeat(60))
// Teste com processo que encontramos antes
const processoNumero = '1000044-50.2025.8.26.0220'
console.log(`\n📋 Buscando: ${processoNumero}`)
try {
const data = await buscarDataJud(processoNumero, 'TJSP')
const hits = data.hits?.hits || []
if (hits.length === 0) {
console.log('❌ Processo não encontrado')
return
}
const processo = hits[0]._source
console.log(`\n✅ Processo encontrado!`)
console.log(` Classe: ${processo.classe?.nome}`)
console.log(` Órgão: ${processo.orgaoJulgador?.nome}`)
console.log(` Total movimentos: ${processo.movimentos?.length || 0}`)
// Filtrar publicações
const publicacoes = (processo.movimentos || []).filter((m: Movimento) =>
m.codigo === 92 || // Publicação
m.nome?.toLowerCase().includes('publicação') ||
m.nome?.toLowerCase().includes('intimação') ||
m.nome?.toLowerCase().includes('citação')
)
console.log(`\n📰 Publicações/Intimações (${publicacoes.length}):`)
publicacoes.slice(0, 10).forEach((m: Movimento, i: number) => {
console.log(` ${i + 1}. ${m.dataHora?.substring(0, 10)} | ${m.nome}`)
})
} catch (error) {
console.error('❌ Erro:', error)
}
}
main()