npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

boletins

v0.47.0

Published

CLI/TUI para indexar, baixar, extrair e buscar boletins administrativos do Portal IFRN

Downloads

28

Readme

boletins

CLI/TUI para indexar, baixar, extrair texto e buscar boletins administrativos do Portal IFRN.

Espelha a árvore de pastas do portal, baixa os PDFs, extrai o texto de cada página e permite buscar uma frase exata em todo o acervo — com prévia da página encontrada, abertura direta no ponto do resultado (local ou remoto) e recorte de intervalos de páginas em um novo PDF.

Publicação (mantenedores)

npm publish

Um único comando: o hook prepack do package.json já roda npm run build automaticamente antes de empacotar e publicar — não é preciso buildar à parte. Para conferir o que seria publicado sem publicar de fato:

npm pack --dry-run

Requisitos

  • Node.js >= 22.5.0

Uso

Sem instalar nada, rode a partir do diretório onde quer manter os dados baixados (cria uma pasta data/ ali):

npx boletins

Isso abre a TUI em tela cheia, já na tela de busca. Na primeira execução, use F12 para ir a Configurações → Indexar (baixa os boletins do portal) e depois Extrair texto (processa os PDFs baixados) antes de buscar.

Para instalar de forma persistente:

npm install -g boletins
boletins

Atalhos na tela de busca

| Tecla | Ação | | --------- | ------------------------------------------------------------ | | Tab | Move entre os campos (frase, página inicial, ano) | | Enter | Busca / confirma a opção selecionada | | Esc | Limpa a frase buscada; se já estiver vazia, pergunta se deseja encerrar | | F12 | Abre a tela de Configurações, mesmo com um campo em foco | | | Navega pelos resultados / opções de um menu |

Modo comando direto (scriptável)

boletins search "termo exato"           # busca direto, sem abrir o formulário

boletins exec [campus]                  # Passos 1 + 2: indexa e depois extrai
boletins exec index [campus]            # Passo 1: indexa e baixa os PDFs novos
boletins exec extract [campus] [--force] # Passo 2: extrai o texto dos PDFs baixados

boletins config                         # mostra a configuração atual
boletins config --start-url <url>            # define a URL de partida da indexação
boletins config --search-start-page <numero> # ignora matches antes dessa página

campus, quando informado, restringe o comando a um único campus (slug usado nas URLs do Portal IFRN, ex.: apodi, natalcentral, ipanguacu). Omitido, o comando processa todos os campi do registro. Um slug inválido faz o comando listar os slugs válidos e sair com erro.

--force (só em exec extract) reprocessa todos os PDFs já baixados, mesmo os que já tiveram texto extraído — por padrão, só os pendentes são processados.

bulletin também funciona como alias do comando (nome usado antes do pacote ser publicado como boletins).

Sincronização remota (bulletin sync)

O Portal IFRN rejeita (503) requisições vindas de fora da rede do IFRN — então exec index só funciona rodado de dentro dela. Se o servidor onde este app está publicado (ver Docker abaixo) não está nessa rede, ele nunca vai conseguir indexar sozinho. bulletin sync existe pra isso: você indexa (e, se quiser, extrai) numa máquina com acesso ao portal, e envia só o que falta pro servidor — sem precisar substituir o banco dele inteiro nem tirá-lo do ar.

No servidor, uma vez, gera a chave de sincronização (fica só o hash gravado no banco — nenhuma env var nem redeploy necessários; rodar de novo troca a chave e invalida a anterior):

docker compose exec boletins node dist/cli/main.js auth

Na máquina com acesso ao portal, depois de indexar (e, se quiser, extrair localmente):

boletins exec index                      # precisa estar na rede do IFRN
boletins up --token <chave-gerada-no-auth> --url https://boletins.exemplo.com.br
# ou: BULLETIN_SYNC_URL=https://boletins.exemplo.com.br boletins up --token <chave>

auth/up são atalhos de sync auth/sync up (os dois nomes funcionam, chamam o mesmo comando). auth já imprime o comando up pronto, com a chave, a flag --token e a URL preenchidas — basta copiar. No Coolify a URL sai certa sozinha, via COOLIFY_FQDN (ver Variáveis de ambiente); fora dele, defina BULLETIN_PUBLIC_URL manualmente. Sem nenhuma das duas, auth chuta http://localhost:<PORT> e avisa que é só um palpite — confira antes de copiar. Se --token for omitido, o comando pede a chave interativamente (digitação mascarada). Ele primeiro pergunta ao servidor o que já está sincronizado e envia só pastas/arquivos novos ou alterados; interrompeu no meio (rede caiu, Ctrl+C)? É só rodar up de novo — ele retoma sozinho, sem reenviar o que já chegou.

Se você não rodou exec extract antes de sincronizar (pra não gastar tempo/CPU da sua máquina extraindo), a busca só funciona depois de extrair o texto — o que já pode ser feito no próprio servidor, porque exec extract não depende do portal, só dos PDFs já no disco:

docker compose exec boletins node dist/cli/main.js exec extract

Docker

O projeto tem um único Dockerfile (build multi-stage) que sobe o servidor web (busca e resultados); indexação e extração continuam manuais, via CLI dentro do container. A imagem não fixa NODE_ENV — quem sobe o container decide o ambiente (ver Variáveis de ambiente abaixo).

Build e subida via Compose (usa NODE_ENV=development, definido em docker-compose.yml):

docker compose up --build

Isso expõe o servidor web em http://localhost:3000 e persiste os dados (banco SQLite, PDFs baixados, textos extraídos) no volume boletins-data, montado em /data.

Build manual da imagem:

docker build -t boletins .
docker run -p 3000:3000 -e NODE_ENV=production -v boletins-data:/data boletins

Para indexar/extrair dentro de um container já em execução:

docker compose exec boletins node dist/cli/main.js exec

Deploy no Coolify

O Dockerfile já define ENV BULLETIN_DATA_DIR=/data e VOLUME /data, mas o Coolify não persiste automaticamente um volume anônimo entre deploys — é preciso criar manualmente, na aba Storages do recurso, um volume persistente chamado data apontando para o path de destino /data. Sem isso, o banco SQLite e os PDFs baixados são perdidos a cada novo deploy.

NODE_ENV=production também precisa ser definida manualmente, na aba Environment Variables do recurso — a imagem não a define sozinha.

Variáveis de ambiente

| Variável | Padrão | Descrição | | -------------------- | ------------- | -------------------------------------------------------------------------- | | BULLETIN_DATA_DIR | (heurística*) | Diretório onde ficam o banco SQLite, os PDFs baixados e os textos extraídos. Definida automaticamente como /data nas imagens Docker. | | PORT | 3000 | Porta em que o servidor web escuta. | | NODE_ENV | — | Não definida pela imagem. Configure production no Coolify (ou -e NODE_ENV=production no docker run); docker-compose.yml já define development para uso local. | | BULLETIN_SYNC_URL | — | Só na máquina que roda bulletin up: URL do servidor de destino, alternativa a --url. | | BULLETIN_SYNC_MAX_FILE_BYTES | 314572800 (300 MB) | Só no servidor: tamanho máximo aceito por arquivo em /sync/files. | | BULLETIN_PUBLIC_URL | $COOLIFY_FQDN, senão http://localhost:<PORT> | Só no servidor: URL pública deste servidor, usada no comando pronto que bulletin auth imprime. No Coolify normalmente não precisa definir — auth já usa COOLIFY_FQDN (injetada automaticamente pelo Coolify quando o recurso tem domínio configurado na aba Domains) sozinho. Defina manualmente fora do Coolify, ou se o app tiver mais de um domínio e nenhum for o certo para sincronização. |

* Fora de containers, sem BULLETIN_DATA_DIR definida: em um checkout do repositório (com .git), os dados ficam em data/ na raiz do projeto; via pacote instalado (npm install -g ou npx), ficam na pasta de dados do usuário do sistema operacional (%APPDATA%\boletins no Windows, ~/Library/Application Support/boletins no macOS, $XDG_DATA_HOME/boletins ou ~/.local/share/boletins no Linux).

Licença

MIT