Documentação Técnica

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.

01

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.

Notas com Editor Rico TipTap + SQLite, área admin protegida por JWT
Games ao Vivo Steam API com dados de jogos em tempo real
Deploy Automatizado git push → build → restart em segundos
🔒 TLS Automático Traefik + Let's Encrypt sem configuração manual
02

Stack de Tecnologias

Aplicação

Tecnologia Versão Função
Astro 4.x Framework SSR — server-side rendering
@astrojs/node Adapter Node.js para modo SSR
React 18.x Componentes interativos (ilhas Astro)
Tailwind CSS 3.x Estilização utilitária
TipTap 2.x Editor de texto rico para as Notas
better-sqlite3 Banco de dados SQLite local
jose JWT para autenticação
TypeScript Tipagem estática

Infraestrutura

Tecnologia Versão Função
Ubuntu 24.04 LTS Sistema operacional do VPS
Docker 29.x Orquestração de containers
Traefik v3.6 Reverse proxy + TLS automático (Let's Encrypt)
Nginx alpine Container Docker — proxy para o Node.js
PM2 6.x Gerenciador de processos Node.js
Node.js v24.x Runtime (via nvm)
Git 2.x Controle de versão + trigger de deploy

Desenvolvimento

Ferramenta Função
Claude Code CLI de desenvolvimento assistido por IA (Anthropic)
VS Code Editor de código
nvm Gerenciador de versões do Node.js
03

Arquitetura

O tráfego percorre quatro camadas antes de chegar à aplicação. Cada camada tem uma responsabilidade única e bem definida.

🌐
Visitante HTTPS / porta 443
Traefik v3.6
Reverse Proxy + TLS Let's Encrypt automático — portas 80 e 443
Nginx alpine
Container Docker proxy_pass para host.docker.internal:4321
Astro SSR
Node.js / PM2 — porta 4321 Server-side rendering, rotas de API, autenticação JWT
SQLite
Banco de dados local Notas
Steam API
API externa Dados de jogos em tempo real

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).
04

Como Rodar Localmente

Pré-requisitos: Node.js 18+, npm, Git.

bash Clone e instale
# 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:

env .env
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.

05

Deploy

O deploy é totalmente automatizado via um Git hook no servidor. Basta fazer push para o remote de produção.

bash Fluxo de deploy
# 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. 1
    Checkout do código O código é extraído do repositório bare para /root/WebSite/source
  2. 2
    npm ci Instala dependências de forma determinística a partir do package-lock.json
  3. 3
    npm run build Gera o bundle SSR otimizado para produção
  4. 4
    pm2 restart website Reinicia 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.

06

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.

07

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.

Coordenador site-orchestrator

Interpreta pedidos em linguagem natural e delega para os agentes corretos. Ponto de entrada para toda interação com o projeto.

Frontend web-dev-craftsman

Páginas Astro, componentes React, estilos, configurações de build, commits e push para produção.

Infra vps-devops-manager

Nginx, Docker, PM2, pipeline de deploy, diagnósticos do servidor via SSH.

QA qa-bug-hunter

Inspeciona o site em busca de bugs visuais, erros funcionais e regressões após deploys.

Segurança security-auditor

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.

08

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.

DESENVOLVEDOR
git push production main
SSH + Git
SERVIDOR VPS
Hook post-receive
npm ci → build → pm2 restart
Node.js SSR (PM2 :4321)
Nginx (Docker)
Traefik + TLS (Let's Encrypt)
HTTPS
🌐
VISITANTE

7.1 Preparar o Servidor VPS

bash Servidor — instalar Node.js e PM2
# 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

bash Servidor — criar repositório 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

bash Servidor — criar post-receive hook
nano /root/repos/meusite.git/hooks/post-receive

Conteúdo do arquivo:

bash hooks/post-receive
#!/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 ---"
bash Tornar executável
chmod +x /root/repos/meusite.git/hooks/post-receive

7.4 Configurar Nginx + Traefik com Docker

Crie o arquivo de configuração do Nginx:

nginx /root/MeuSite/nginx/default.conf
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:

yaml /root/MeuSite/docker-compose.yml
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
bash Subir o container
docker compose up -d

7.5 Criar o Projeto Astro

bash Local — criar projeto e instalar dependências
# 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

bash Local + Servidor — 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.

09

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.tsDicionários de tradução em TypeScript, tipados a partir do arquivo PT (fonte de verdade).
  • src/i18n/index.tsHelper useTranslations(lang) e getLang(cookies) para leitura segura do cookie.
  • src/pages/api/set-lang.tsEndpoint POST que seta o cookie lang (maxAge: 1 ano) e redireciona de volta via Referer.
  • src/components/layout/LanguageSwitcher.astroTrês botões PT | EN | ES integrados ao Header, com o idioma ativo destacado.

Como funciona em cada página SSR

astro Exemplo de uso em uma página
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.

10

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)
Prompt 1 de 2 Construir o projeto + configurar o servidor

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.

prompt Claude Code — Prompt 1
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
Ação Manual Autorizar a chave SSH no servidor

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.

bash Executar no servidor (via SSH)
# 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
Prompt 2 de 2 Criar .env + deploy + verificação

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.

prompt Claude Code — Prompt 2
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.