GB
Todos os estudos

Moonlight

Plataforma multi-tenant para montar páginas pessoais por blocos, com moderação de imagens por IA (NSFW), upload otimizado e deploy próprio.

Período
2026
Status
Concluído
Repositório
moonlight

Visão geral

Moonlight é um projeto de estudo e portfólio: uma plataforma multi-tenant em que cada cliente monta sua página a partir de blocos (header, hero, links, texto, rodapé) e a publica no próprio subdomínio. É um monorepo pnpm + Turborepo com Next.js 15 no front e NestJS + Prisma sobre PostgreSQL no back. As páginas são guardadas como JSONB versionado e validadas pelo mesmo schema Zod no front e no back. Um dos pontos mais interessantes é o upload de imagens: cada arquivo passa por um pipeline que valida o formato real, reencoda com sharp e passa por uma moderação NSFW rodando localmente, sem depender de API externa. O projeto roda 100% local e sem custo, e está publicado com a API numa VPS (Docker + Caddy) e o front na Vercel.

Stack

  • TypeScript
  • Next.js
  • NestJS
  • Prisma
  • PostgreSQL
  • Redis
  • Zod
  • TensorFlow.js
  • nsfwjs
  • sharp
  • Docker
  • Caddy
  • TanStack Query
  • Turborepo
  • pnpm

Principais entregas

  • Páginas montadas por blocos, armazenadas como JSONB com schemaVersion e validadas pelo mesmo schema Zod no front e no back
  • Multi-tenant por subdomínio (ex.: maria.localhost), com fallback por caminho em /s/[tenant]
  • Moderação de imagens NSFW com nsfwjs (MobileNetV2) + TensorFlow.js, rodando numa worker thread: a imagem é pré-processada com sharp e rejeitada se a soma das notas Porn + Hentai passar de um limite configurável (padrão 0,6)
  • Upload com sharp: detecta o formato real (JPEG, PNG, WebP, GIF), corrige a orientação EXIF, limita o tamanho, descarta metadados e converte tudo para WebP
  • Proteções no upload: rate limit de 40 imagens por hora por usuário, limite de pixels contra decompression bomb e cota por plano
  • Storage atrás de uma interface (hoje disco local), pronto para trocar por um bucket sem mexer no domínio; e-mail e cobrança seguem o mesmo padrão de adapters
  • Cookie de sessão host-only no domínio principal, para que as páginas dos clientes nunca recebam a sessão do admin
  • Redis para cache, filas e contadores, e Mailpit para testar e-mails em desenvolvimento

Desafios técnicos

  • 01

    Rodar um modelo de IA dentro do Node sem travar a API: o TensorFlow.js fica numa worker thread, com timeout por imagem, respawn automático e erros 503 amigáveis

  • 02

    Equilibrar falsos positivos e negativos na moderação, usando o limiar da soma de categorias e permitindo ajustá-lo ou desligar a checagem por variável de ambiente

  • 03

    Isolar os dados de cada cliente numa plataforma multi-tenant e separar a sessão do admin das páginas públicas

  • 04

    Manter front e back sincronizados com contratos compartilhados num monorepo, com Husky, commitlint e um comando de verificação único

  • 05

    Colocar tudo no ar: API, PostgreSQL e Redis em containers com Caddy na VPS e o front na Vercel