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

rl-core-api

v0.14.4

Published

Core NestJS: autenticação com 2FA, RBAC, auditoria, notificações e listagens com filtro dinâmico

Readme

rl-core-api

Base NestJS para não recomeçar todo projeto pelo login.

Instala e o sistema já sobe com autenticação (2FA obrigatório por TOTP), RBAC, trilha de auditoria, notificações em WebSocket e listagens paginadas com filtro dinâmico. Você escreve só o domínio.

npm install rl-core-api

Uso

// src/app.module.ts
import { Module } from "@nestjs/common";
import { RlCoreModule } from "rl-core-api";

import { ContasModule } from "@/features/contas/contas.module";
import { Conta } from "@/features/contas/infra/schema/conta.schema";

@Module({
  imports: [RlCoreModule.forRoot({ entities: [Conta] }), ContasModule],
})
export class AppModule {}
// src/main.ts
import { bootstrapCore } from "rl-core-api";

import { AppModule } from "@/app.module";

void bootstrapCore(AppModule, { title: "Controle Pessoal" });
// src/data-source.ts — a CLI do TypeORM usa este arquivo
import "dotenv/config";
import { coreEntities, coreMigrations, createCoreDataSource } from "rl-core-api";

import { CreateContas1790000000000 } from "@/migrations/1790000000000-CreateContas";
import { Conta } from "@/features/contas/infra/schema/conta.schema";

export const AppDataSource = createCoreDataSource({
  entities: [...coreEntities, Conta],
  migrations: [...coreMigrations, CreateContas1790000000000],
});

npm run migration:run cria as tabelas do core e as suas, na ordem certa. As migrations do core vêm no pacote — você nunca copia nem reescreve nenhuma.

O que vem pronto

| | | |---|---| | Auth | login, refresh por cookie httpOnly, 2FA obrigatório (TOTP + fallback por e-mail + códigos de backup), primeiro acesso, reset de senha, bloqueio por tentativas, expiração de senha | | RBAC | resources, actions, scopes, permissions, roles e groups, com guard por resource:action:scope | | Auditoria | trilha de alterações de dados, log de requisições e log de erros, mantidos indefinidamente (sem retenção automática) | | Notificações | WebSocket com handshake por cookie ou Bearer, escopo resolvido na leitura | | Listagens | paginação, ordenação e filtro dinâmico com catálogo por listagem ($AND/$OR aninhados, 17 operadores) | | Fila | processamento em segundo plano com progresso em tempo real (BullMQ + Redis), opcional por ambiente | | Infra | config validada com Zod, mailer, agendador, rate-limit, CSRF, filtro global de exceções, logger Winston |

Configuração

O pacote não traz .env — ele declara o que precisa e quebra no boot se faltar. Cada projeto tem os valores dele (banco próprio, segredo JWT próprio). Comece pelo .env.example do repositório.

Fila de processamento (opcional)

Sem REDIS_HOST a aplicação sobe sem fila e sem erro — é o padrão. Declarado, o Redis passa a ser obrigatório: a subida falha se ele não responder, em vez de a falha aparecer no primeiro job.

REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=redis
REDIS_PREFIX=controlx   # um Redis para vários projetos: o prefixo os separa

Concorrência, tentativas, backoff e retenção não são variáveis de ambiente — são constantes em queue.constants.ts. São decisão de arquitetura da fila, e um número desses ajustado no .env de produção sem ninguém revisar é como se descobre, tarde, por que os jobs empilharam.

O processador é sempre do projeto — o core não conhece o que a feature faz:

@Processor(JOBS_QUEUE, { concurrency: JOB_CONCURRENCY, autorun: false })
export class FunkosImportProcessor extends JobWorkerHost<Payload, Summary> {
  constructor(
    jobEvents: JobEventsService,
    private readonly imports: FunkosImportService,
  ) {
    super(jobEvents);
  }

  async handle(job: JobContext<Payload>): Promise<Summary> {
    return this.imports.run(job.payload, (done, total) => job.report(done, total));
  }
}

Registre a classe como provider do módulo da feature, atrás do isRedisConfigured() — sem Redis não há conexão para o worker abrir:

@Module({ providers: isRedisConfigured() ? [FunkosImportProcessor] : [] })
export class FunkosModule {}

Na rota, jobs.enqueue(name, payload, { ownerId }) devolve o jobId na hora. O progresso vai para a sala do dono pela mesma conexão de socket das notificações (job:progress, job:completed, job:failed), e GET /jobs/:id responde a quem perdeu o socket.

Quem opera vê tudo pela tela de Filas (QueuesScreen, no rl-core-front): contagem por estado, o motivo cru da falha, reprocessar e remover — com queues:read:any e queues:manage:any.

Identidade do sistema

O core não tem nome próprio. APP_NAME é o do projeto, e dele saem o título do Swagger, o remetente dos emails e o emissor exibido no app autenticador:

| Variável | Para quê | Sem declarar | |---|---|---| | APP_NAME | Nome do sistema | Core App | | APP_LOGO_URL | Logo no cabeçalho dos emails — URL absoluta e pública, porque o cliente de email busca a imagem do servidor dele | só o nome, em texto | | APP_BRAND_COLOR | Cor dos emails — botões, código 2FA e o nome no cabeçalho. Não é a cor da interface, que vem do tailwind-preset. Escreva entre aspas: APP_BRAND_COLOR="#16A34A", senão o dotenv lê o # como comentário e o valor chega vazio | #007bff | | TOTP_ISSUER | Emissor no app autenticador | segue o APP_NAME | | EMAIL_FROM_NAME | Nome do remetente | segue o APP_NAME |

Quem já declarava TOTP_ISSUER ou EMAIL_FROM_NAME continua igual: eles só caem no APP_NAME quando estão ausentes.

Para acrescentar variáveis suas, estenda o schema:

import { envSchema } from "rl-core-api";

export const appEnvSchema = envSchema.extend({
  FUNKO_API_KEY: z.string().min(1),
});

Peer dependencies

@nestjs/common, @nestjs/core, @nestjs/platform-express, @nestjs/swagger, @nestjs/typeorm, class-transformer, class-validator, reflect-metadata, rxjs, typeorm e zod são peers: precisam ser uma instância só, ou a injeção de dependência e os decorators param de se enxergar. O npm 7+ instala sozinho.

Pacotes irmãos

| | | |---|---| | rl-core-front | as telas em Next.js | | rl_core_app | o app Flutter, consumido por tag do git |

Changelog

As três versões andam juntas e saem de uma tag só — o que mudou em cada uma está em CHANGELOG.md.

Licença

MIT © Rodrigo Liberti