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