Sobre o Site
Como este site foi construído — stack, arquitetura, deploy automatizado e um guia completo para qualquer pessoa replicar essa estrutura do zero.
Desenvolvido em colaboração com Claude (Anthropic) usando o Claude Code — uma CLI para desenvolvimento assistido por IA.
Visão Geral
Este é um site pessoal moderno com Server-Side Rendering (SSR) construído em Astro. Vai além de uma simples página estática: inclui um blog com editor de texto rico, área de games integrada com a Steam API em tempo real, e um sistema de deploy completamente automatizado via Git hooks.
Todo o desenvolvimento foi feito em colaboração com Claude (Anthropic) usando o Claude Code — uma interface de linha de comando que permite desenvolvimento assistido por IA diretamente no terminal, com acesso ao sistema de arquivos, Git e servidor.
O projeto utiliza um conjunto de agentes especializados que automatizam tarefas de frontend, infraestrutura, qualidade e segurança — todos coordenados por linguagem natural.
Stack de Tecnologias
Aplicação
| Tecnologia | Versão | Função |
|---|---|---|
| Astro | 4.x | Framework SSR — server-side rendering |
| Escolhido por ser o framework mais eficiente para sites com conteúdo misto — páginas estáticas e dinâmicas coexistindo. O modelo de "ilhas" permite hidratar apenas os componentes interativos, reduzindo o JavaScript enviado ao browser. | ||
| @astrojs/node | — | Adapter Node.js para modo SSR |
| Necessário para rodar o Astro em modo servidor (SSR). Transforma o build em um servidor Node.js standalone que pode ser gerenciado pelo PM2. | ||
| React | 18.x | Componentes interativos (ilhas Astro) |
| Usado apenas onde há interatividade real — o editor TipTap das Notas. O Astro carrega o React apenas nessas "ilhas", sem impacto no restante das páginas. | ||
| Tailwind CSS | 3.x | Estilização utilitária |
| Permite estilização rápida e consistente sem sair do HTML. Elimina o problema de CSS crescendo de forma descontrolada, pois as classes utilitárias são purged no build final. | ||
| TipTap | 2.x | Editor de texto rico para as Notas |
| Editor rico baseado em ProseMirror com API moderna para React. Escolhido pela flexibilidade de extensões, boa documentação e capacidade de exportar HTML limpo para salvar no banco. | ||
| better-sqlite3 | — | Banco de dados SQLite local |
| SQLite síncrono e sem servidor separado — perfeito para um site pessoal com Notas. Zero configuração de infraestrutura, o banco é um único arquivo no servidor. | ||
| jose | — | JWT para autenticação |
| Biblioteca leve e moderna para JWT que funciona tanto no Node.js quanto em ambientes edge. Usada para gerar e verificar tokens de sessão da área admin. | ||
| TypeScript | — | Tipagem estática |
| Adiciona tipagem estática ao JavaScript, prevenindo erros em tempo de desenvolvimento. No Astro, é suportado nativamente sem configuração extra. | ||
Infraestrutura
| Tecnologia | Versão | Função |
|---|---|---|
| Ubuntu | 24.04 LTS | Sistema operacional do VPS |
| Sistema operacional estável e amplamente suportado para servidores. A versão LTS garante suporte de segurança por 5 anos. | ||
| Docker | 29.x | Orquestração de containers |
| Permite rodar o Nginx em um container isolado sem conflitar com outros serviços já existentes no servidor (n8n, Traefik). Facilita atualizações e rollback. | ||
| Traefik | v3.6 | Reverse proxy + TLS automático (Let's Encrypt) |
| Já estava no servidor para gerenciar o n8n. Detecta containers Docker automaticamente via labels e provisiona certificados TLS pelo Let's Encrypt sem configuração manual. | ||
| Nginx | alpine | Container Docker — proxy para o Node.js |
| Atua como proxy reverso dentro do Docker, recebendo as requisições do Traefik e encaminhando para o processo Node.js na porta 4321 do host via host.docker.internal. | ||
| PM2 | 6.x | Gerenciador de processos Node.js |
| Mantém o processo Node.js do Astro sempre rodando. Se o processo cair, o PM2 reinicia automaticamente. Também persiste os processos entre reboots do servidor. | ||
| Node.js | v24.x | Runtime (via nvm) |
| Runtime necessário para executar o servidor Astro SSR e rodar o build. Instalado via nvm para facilitar troca de versões sem conflitos com o sistema operacional. | ||
| Git | 2.x | Controle de versão + trigger de deploy |
| Além do controle de versão, serve como mecanismo de deploy. O hook post-receive no repositório bare dispara o build automaticamente a cada git push. | ||
Desenvolvimento
| Ferramenta | Função |
|---|---|
| Claude Code | CLI de desenvolvimento assistido por IA (Anthropic) |
| CLI da Anthropic que permite ao Claude interagir diretamente com o sistema de arquivos, terminal e Git. Todo o desenvolvimento deste site foi feito através de conversas em linguagem natural com agentes especializados. | |
| VS Code | Editor de código |
| Editor principal para revisão do código gerado pelos agentes, com suporte nativo a TypeScript, Astro e integração com o terminal onde o Claude Code roda. | |
| nvm | Gerenciador de versões do Node.js |
| Gerencia múltiplas versões do Node.js no servidor sem conflitos com o sistema. Essencial para que o hook de deploy encontre o Node.js correto em uma sessão não-interativa do bash. | |
Arquitetura
O tráfego percorre quatro camadas antes de chegar à aplicação. Cada camada tem uma responsabilidade única e bem definida.
Por que cada camada?
- Traefik — gerencia TLS e certificados Let's Encrypt automaticamente. Sem ele, renovar HTTPS seria um processo manual e propenso a erros.
- Nginx — atua como buffer e proxy entre o mundo externo e o processo Node.js. Permite configurações de headers, caching e logs independentes da aplicação.
- PM2 — garante que o processo Node.js reinicie automaticamente após falhas ou reinicializações do servidor.
- Astro SSR — renderiza HTML no servidor, garantindo boa performance, SEO correto e suporte a lógica dinâmica (banco de dados, autenticação).
Como Rodar Localmente
Pré-requisitos: Node.js 18+, npm, Git.
# 1. Clonar o repositório
git clone https://github.com/lescuciato/EscuciatoWebSitePersonal.git
cd EscuciatoWebSitePersonal
# 2. Instalar dependências
npm install
# 3. Configurar variáveis de ambiente
cp .env.example .env
# Editar .env com seus valores
# 4. Rodar em desenvolvimento
npm run dev
# Acesse: http://localhost:4321 Variáveis de ambiente
Crie um arquivo .env na raiz do projeto com as seguintes variáveis:
ADMIN_PASSWORD=sua-senha-admin
JWT_SECRET=chave-secreta-jwt-min-32-chars
STEAM_API_KEY=sua-chave-api-steam # opcional
STEAM_ID=seu-steam-id-64bits # opcional
HOST=0.0.0.0
PORT=4321 As variáveis STEAM_API_KEY e STEAM_ID são opcionais. Sem elas, a página de Games exibirá um estado vazio mas o restante do site funcionará normalmente.
Deploy
O deploy é totalmente automatizado via um Git hook no servidor. Basta fazer push para o remote de produção.
# Configurar remote de produção (uma única vez)
git remote add production root@SEU_SERVIDOR:/root/repos/website.git
# Deploy: commitar e enviar
git add .
git commit -m "feat: descrição das mudanças"
git push production main O que acontece automaticamente no servidor
O hook post-receive executa em sequência:
- 1 Checkout do código O código é extraído do repositório bare para
/root/WebSite/source - 2
npm ciInstala dependências de forma determinística a partir dopackage-lock.json - 3
npm run buildGera o bundle SSR otimizado para produção - 4
pm2 restart websiteReinicia o processo Node.js com zero downtime percebido
O arquivo .env no servidor deve existir antes do primeiro build. Se você adicionar novas variáveis ao projeto, é necessário atualizá-las no servidor e fazer um novo deploy.
Persistência após reboot
O processo Node.js é gerenciado pelo PM2 com um arquivo ecosystem.config.cjs que força HOST=0.0.0.0. O serviço pm2-root.service está registrado no systemd, garantindo que o PM2 — e com ele o site — suba automaticamente após qualquer reboot do servidor. Os containers Docker (Nginx, Traefik, n8n) também possuem política restart=always.
6. Segurança
A área administrativa do site é protegida por uma camada de autenticação JWT. Abaixo estão as práticas de segurança adotadas.
Cookies de Sessão Seguros
Os cookies de autenticação utilizam o atributo Secure, garantindo que só sejam transmitidos em conexões HTTPS.
Prevenção de XSS
O conteúdo dos posts das Notas passa por sanitização via lista de permissões (allowlist) antes de ser renderizado, bloqueando scripts maliciosos.
Respostas Genéricas de Erro
As respostas de erro são intencionalmente genéricas para não expor detalhes internos do sistema.
Rate Limiting no Login
O endpoint de login possui limitação de tentativas para dificultar ataques de força bruta.
Expiração de Sessão
As sessões expiram após um período limitado de tempo, reduzindo a janela de exposição em caso de token comprometido.
Invalidação de Token no Logout
Os tokens são explicitamente invalidados ao sair, impedindo que sejam reutilizados mesmo que tenham sido capturados.
Comparação em Tempo Constante
A verificação de senha usa comparação em tempo constante para prevenir ataques de temporização.
Proteção contra Redirecionamento Aberto
Os parâmetros de redirecionamento são validados para evitar que um atacante redirecione usuários para domínios externos.
Agentes Claude Code
Este projeto utiliza Claude Code com um sistema de agentes especializados definidos em .claude/agents/. Cada agente tem um domínio de responsabilidade claro, e todos são coordenados por linguagem natural através do site-orchestrator.
Interpreta pedidos em linguagem natural e delega para os agentes corretos. Ponto de entrada para toda interação com o projeto.
Páginas Astro, componentes React, estilos, configurações de build, commits e push para produção.
Nginx, Docker, PM2, pipeline de deploy, diagnósticos do servidor via SSH.
Inspeciona o site em busca de bugs visuais, erros funcionais e regressões após deploys.
Audita o repositório por credenciais expostas, dados sensíveis e vulnerabilidades.
O Claude Code não substitui o desenvolvedor — ele amplifica a capacidade de execução. Decisões de arquitetura, revisão de código e validação final sempre ficam com o humano.
Guia: Construa um Site Igual do Zero
Passo a passo para replicar essa arquitetura completa em qualquer VPS. Os placeholders SEU_SERVIDOR, SEU_DOMINIO e meusite devem ser substituídos pelos seus valores reais.
git push production main npm ci → build → pm2 restart 7.1 Preparar o Servidor VPS
# Instalar Node.js via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install --lts
# Instalar PM2 globalmente
npm install -g pm2
# Verificar Git (geralmente já instalado)
git --version 7.2 Configurar o Repositório Git Bare
# Criar repositório bare para receber pushes
mkdir -p /root/repos/meusite.git
cd /root/repos/meusite.git
git init --bare
# Criar diretório de trabalho
mkdir -p /root/MeuSite/source 7.3 Criar o Hook de Deploy
nano /root/repos/meusite.git/hooks/post-receive Conteúdo do arquivo:
#!/bin/bash
set -e
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
REPO="/root/repos/meusite.git"
WORK_DIR="/root/MeuSite/source"
echo "--- Deploy iniciado ---"
mkdir -p "$WORK_DIR"
git --work-tree="$WORK_DIR" --git-dir="$REPO" checkout -f main
cd "$WORK_DIR"
npm ci
npm run build
pm2 restart meusite || pm2 start npm --name "meusite" -- run start
pm2 save
echo "--- Deploy concluído ---" chmod +x /root/repos/meusite.git/hooks/post-receive 7.4 Configurar Nginx + Traefik com Docker
Crie o arquivo de configuração do Nginx:
server {
listen 80;
location / {
proxy_pass http://host.docker.internal:4321;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_bypass $http_upgrade;
}
} Crie o Docker Compose:
services:
website:
image: nginx:alpine
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.website.rule=Host(`SEU_DOMINIO`)"
- "traefik.http.routers.website.entrypoints=websecure"
- "traefik.http.routers.website.tls.certresolver=mytlschallenge"
networks:
- traefik_network
networks:
traefik_network:
external: true docker compose up -d 7.5 Criar o Projeto Astro
# Criar projeto Astro
npm create astro@latest meu-site
cd meu-site
# Dependências necessárias
npm install @astrojs/node @astrojs/react @astrojs/tailwind
npm install better-sqlite3 jose
npm install @tiptap/react @tiptap/starter-kit @tiptap/extension-link
# Configurar astro.config.mjs para SSR com adapter Node 7.6 Configurar Remote e Fazer o Primeiro Deploy
# Adicionar o remote de produção (local)
git remote add production root@SEU_SERVIDOR:/root/repos/meusite.git
# Criar .env no servidor ANTES do primeiro deploy
ssh root@SEU_SERVIDOR "cat > /root/MeuSite/source/.env << 'EOF'
ADMIN_PASSWORD=sua-senha-segura
JWT_SECRET=sua-chave-secreta-com-32-chars-minimo
HOST=0.0.0.0
PORT=4321
EOF"
# Primeiro deploy
git push production main Após o push, o hook executa automaticamente. Em menos de um minuto seu site estará disponível em https://SEU_DOMINIO com TLS configurado.
Internacionalização (i18n)
O site suporta três idiomas — Português (PT), English (EN) e Español (ES) — sem alterar as URLs. A troca de idioma é feita via cookie lang, lido no SSR a cada renderização.
Arquitetura
-
src/i18n/pt.ts,en.ts,es.ts— Dicionários de tradução em TypeScript, tipados a partir do arquivo PT (fonte de verdade). -
src/i18n/index.ts— HelperuseTranslations(lang)egetLang(cookies)para leitura segura do cookie. -
src/pages/api/set-lang.ts— Endpoint POST que seta o cookielang(maxAge: 1 ano) e redireciona de volta viaReferer. -
src/components/layout/LanguageSwitcher.astro— Três botõesPT | EN | ESintegrados ao Header, com o idioma ativo destacado.
Como funciona em cada página SSR
import { getLang, useTranslations } from '../i18n/index';
const lang = getLang(Astro.cookies); // 'pt' | 'en' | 'es'
const t = useTranslations(lang);
// Usar nas templates:
// {t.nav.home} → 'Home' / 'Home' / 'Inicio'
// {t.hero.contact} → 'Contato' / 'Contact' / 'Contacto' As páginas de administração (/notas/admin, /notas/new) permanecem em português — são áreas internas sem necessidade de internacionalização. As APIs também não foram alteradas.
Usando AI
Todo este site foi construído usando o Claude Code — uma CLI que permite que um modelo de IA leia arquivos, escreva código, acesse o servidor via SSH e gerencie o ciclo completo de desenvolvimento. O processo humano foi quase inteiramente de direção: descrever o que queria, revisar o resultado e aprovar ou pedir ajustes.
Com os dois prompts abaixo, qualquer pessoa pode replicar este projeto completo — do zero ao site no ar — com no máximo 1 comando manual.
Antes de começar — o que você precisa ter
- ✓ Claude Code instalado —
npm install -g @anthropic-ai/claude-code - ✓ Node.js 20+ local — para rodar o projeto durante o desenvolvimento
- ✓ VPS com Ubuntu 24.04 — Docker instalado, Traefik rodando com um wildcard/domínio configurado
- ✓ Acesso root ao servidor via SSH — o agente vai precisar executar comandos no servidor
- ? Chave da Steam API — opcional, só se quiser a integração com Steam na página de Games
Tenha em mãos antes de iniciar:
- IP do seu servidor e senha root
- Domínio ou subdomínio que aponta para o servidor
- Seu currículo / experiências profissionais (texto ou PDF)
- Uma senha forte para o admin das Notas (misture maiúsculas, minúsculas, números e símbolos) e uma chave JWT (string aleatória com mínimo 32 caracteres)
- Steam ID ou vanity URL (opcional)
Cole este prompt no Claude Code a partir de uma pasta vazia no seu computador. O agente vai criar o projeto completo localmente e configurar o servidor — ao final ele vai exibir a chave SSH pública que precisa ser autorizada no servidor.
Quero construir um site pessoal usando Astro SSR e publicá-lo em um VPS.
Ao final deste prompt você vai ter:
1. O projeto completo rodando localmente
2. O servidor configurado com Nginx + PM2 + pipeline de deploy via Git hook
3. Exibida na tela a chave SSH pública que precisa ser autorizada no servidor
Informações do projeto:
- Meu nome: [SEU NOME]
- Profissão atual: [SUA PROFISSÃO]
- Cidade: [SUA CIDADE]
- Hobbies: [HOBBIES]
- Experiências profissionais: [COLE AQUI OU INFORME CAMINHO DO CV]
Informações do servidor:
- IP: [IP_DO_SERVIDOR]
- Usuário: root
- Domínio/host: [SEU_DOMINIO]
- Traefik já está rodando no servidor com Let's Encrypt configurado
Stack desejada (não alterar):
- Astro SSR com @astrojs/node adapter (standalone)
- React para componentes interativos
- TipTap para o editor de Notas
- SQLite (better-sqlite3) para os posts
- JWT (biblioteca jose) para autenticação do admin
- PM2 para gerenciar o processo Node.js
- Nginx (Docker) como proxy reverso para a porta 4321
- sanitize-html para sanitização de conteúdo do blog
Páginas que o site deve ter:
1. Home — apresentação pessoal com hobbies
2. Profissional — timeline de carreira com tooltip no hover
3. Notas — listagem de posts + editor admin com TipTap (protegido por JWT)
4. Games — integração opcional com Steam API (seção pode ficar vazia por ora)
5. Sobre o Site — documentação técnica do projeto
Requisitos de segurança obrigatórios (aplicar todos):
- Rate limiting no endpoint de login para dificultar ataques de força bruta
- Invalidação de token JWT no logout (revogar no servidor, não apenas apagar o cookie)
- Comparação de senha em tempo constante (crypto.timingSafeEqual) para prevenir timing attacks
- Cookie de sessão com flag Secure em conexões HTTPS
- Sanitização do HTML do blog com allowlist via sanitize-html antes de renderizar
- Validação de parâmetros de redirecionamento para prevenir open redirect
- Respostas de erro genéricas para não expor detalhes internos do sistema
- Sessão com expiração de tempo limitado
Tarefa dos agentes:
- web-dev-craftsman: criar todo o projeto Astro localmente com a stack acima, aplicando todos os requisitos de segurança
- vps-devops-manager: configurar o servidor (repositório bare + hook de deploy + Nginx Docker + PM2)
- security-auditor: verificar o código gerado antes do primeiro deploy
Ao finalizar:
- Mostrar o conteúdo do arquivo ~/.ssh/id_ed25519.pub (chave SSH pública)
- Instruir o que precisa ser feito antes do Prompt 2 Esta é a única etapa manual. O Claude Code não pode se autorizar no servidor sem que você execute este comando uma vez. Substitua pela chave exibida no Prompt 1.
# Conectar ao servidor
ssh root@[IP_DO_SERVIDOR]
# Adicionar a chave SSH pública do Claude Code
echo "COLE_AQUI_A_CHAVE_PUBLICA_DO_PROMPT_1" >> ~/.ssh/authorized_keys
# Sair do servidor
exit Após autorizar a chave SSH, execute este segundo prompt na mesma sessão do Claude Code (ou em uma nova, dentro da pasta do projeto). O agente vai criar o arquivo .env no servidor, fazer o primeiro deploy e verificar se o site está no ar.
A chave SSH já foi autorizada no servidor. Agora execute as etapas finais:
1. vps-devops-manager: criar o arquivo .env no servidor em /root/MeuSite/source/.env com:
ADMIN_PASSWORD=[SENHA_FORTE — ex: MinhaS3nh@2025! — use maiúsculas, minúsculas, números e símbolos]
JWT_SECRET=[STRING_ALEATÓRIA_COM_MÍNIMO_32_CARACTERES — ex: gere com: openssl rand -base64 48]
HOST=0.0.0.0
PORT=4321
(Se quiser Steam: STEAM_API_KEY=[SUA_CHAVE_STEAM] e STEAM_ID=[SEU_STEAM_ID])
2. web-dev-craftsman: adicionar o remote de produção e fazer o primeiro deploy:
git remote add production root@[IP_DO_SERVIDOR]:/root/repos/meusite.git
git push production main
3. vps-devops-manager: verificar que o PM2 subiu corretamente após o deploy
4. security-auditor: verificar que não há credenciais expostas no repositório
e que o fluxo de autenticação em produção está funcionando corretamente
5. qa-bug-hunter: acessar [SEU_DOMINIO] e verificar que o site está funcionando —
checar home, notas, profissional e games
Ao final, exibir a URL pública do site. Pronto — site no ar com deploy automatizado. A partir daí, qualquer mudança é feita descrevendo em linguagem natural e confirmada com git push production main.