BlogSharp é uma API backend para um Blog Pessoal, desenvolvida em ASP.NET Core 8, PostgreSQL, Docker, JWT, integração com IA e análise de qualidade com SonarQube.
Eu mantive este README focado em duas coisas: como rodar o projeto e quais decisões técnicas foram tomadas. Endpoints e contratos detalhados ficam no Swagger, porque ele reflete a API executável.
- 1. Pré-requisitos
- 2. Configuração do ambiente
- 3. Executando a aplicação
- 4. Swagger e autenticação
- 5. Banco de dados e migrations
- 6. Seeders
- 7. Integração com IA
- 8. Testes
- 9. SonarQube
- 10. Decisões do projeto
- 11. Licença
- Docker
- Docker Compose v2
- .NET SDK 8, para rodar testes, migrations manuais ou SonarQube pela máquina local
dotnet-sonarscanner, apenas para executar a análise do SonarQube
Crie o arquivo .env na raiz do projeto usando o .env.example como base:
cp .env.example .envConfiguração mínima para desenvolvimento local:
POSTGRES_DB=blogsharp
POSTGRES_USER=blogsharp
POSTGRES_PASSWORD=blogsharp_password
JWT_SECRET_KEY=troque_por_uma_chave_segura_com_pelo_menos_32_caracteres
IA_ENABLED=false
IA_PROVIDER=OpenRouter
IA_BASE_URL=https://openrouter.ai
IA_API_KEY=
IA_MODEL=
IA_SITE_URL=
IA_APP_NAME=BlogSharp
HOST_UID=1000
HOST_GID=1000
SONAR_TOKEN=HOST_UID e HOST_GID são usados apenas para evitar problema de permissão em arquivos gerados pelo container no Linux. Na maioria das instalações Linux o valor 1000 já funciona; se der problema de permissão em bin/, obj/ ou arquivos gerados pelo Docker, confira os valores com:
id -u
id -gSempre que alterar variáveis do .env, recrie o container da API para que o Docker Compose injete os novos valores:
docker compose up -d --force-recreate apiNa raiz do projeto, suba a API e o PostgreSQL:
docker compose up --buildPostgreSQL: localhost:5432
Para parar os containers:
docker compose downPara parar e remover também os volumes locais:
docker compose down -vUse down -v apenas quando quiser apagar os dados locais do PostgreSQL.
O Swagger pode ser acessado em http://localhost:5000/swagger/index.html.
Para testar rotas protegidas:
- Faça login no endpoint
POST /api/usuarios/login. - Copie o valor retornado no campo
token. - Clique em
Authorizeno Swagger. - Cole apenas o token e confirme.
O projeto usa PostgreSQL como banco relacional. Dentro da rede do Docker Compose, a API acessa o banco pelo host database.
Os dados ficam no volume Docker blogsharp_postgres_data.
No fluxo normal com Docker, não é necessário rodar migrations manualmente. Ao iniciar em ambiente de desenvolvimento, os seeders aplicam migrations pendentes antes de inserir os dados iniciais.
Se eu quiser aplicar migrations fora desse fluxo automático, com o PostgreSQL já rodando, uso:
ASPNETCORE_ENVIRONMENT=Development dotnet ef database update --project src/BlogSharp.Api/BlogSharp.Api.csprojEu mantive dois modos de seed: um automático para dados mínimos de desenvolvimento e outro manual para criar massa de dados quando for necessário testar melhor a aplicação.
Quando a API inicia em ambiente Development, os seeders fixos executam:
- aplicam migrations pendentes;
- criam usuários fixos se os emails ainda não existirem;
- criam um tema inicial se ele ainda não existir;
- criam uma postagem inicial se ela ainda não existir.
Usuários disponíveis para testes:
| Perfil | Senha | |
|---|---|---|
| Admin | admin@blogsharp.com |
Admin@123 |
| Usuario | usuario@blogsharp.com |
Usuario@123 |
Para criar dados aleatórios, use os comandos abaixo com a API em execução:
docker compose exec api dotnet run -- seed usuarios 10
docker compose exec api dotnet run -- seed temas 5
docker compose exec api dotnet run -- seed postagens 20O número final define a quantidade de registros que será criada.
Usuários aleatórios usam emails únicos no domínio seed.blogsharp.local e senha padrão:
Senha@123
As postagens aleatórias são vinculadas a usuários e temas já cadastrados. Se os dados fixos ainda não existirem, o seeder manual de postagens cria antes os usuários e o tema inicial.
Quando a integração está habilitada, ao cadastrar uma postagem a API envia o conteúdo para um provedor externo de IA e salva na postagem:
- resumo;
- tags;
- categoria.
A integração fica desabilitada por padrão:
IA_ENABLED=falseCom a IA desabilitada, o cadastro de postagens continua funcionando e os campos ResumoIA, TagsIA e CategoriaIA ficam vazios.
Para habilitar:
IA_ENABLED=true
IA_PROVIDER=OpenRouter
IA_BASE_URL=https://openrouter.ai
IA_API_KEY=sua_chave_da_openrouter
IA_MODEL=openai/gpt-5.2
IA_SITE_URL=http://localhost:5000
IA_APP_NAME=BlogSharpDepois de alterar o .env, recrie o container da API:
docker compose up -d --force-recreate apiTambém existe a rota protegida POST /api/ia/resumir, usada para gerar resumo, categoria e tags a partir de um texto sem criar uma postagem.
Os testes ficam em tests/BlogSharp.Api.Tests.
Para executar todos os testes:
dotnet test BlogSharp.slnPara executar apenas os testes de integração:
dotnet test BlogSharp.sln --filter FullyQualifiedName~IntegrationOs testes unitários cobrem regras de service e provider de IA com fakes simples. Os testes de integração sobem a API em memória e validam fluxos HTTP principais.
O projeto possui um serviço Docker para rodar o SonarQube localmente. Ele fica no profile quality para não subir junto com API e banco quando eu só quero desenvolver ou testar a aplicação.
docker compose --profile quality up -d sonarqubePainel: http://localhost:9000
No primeiro acesso, use:
Login: admin
Senha: admin
O SonarQube irá solicitar a troca da senha.
Crie um token no SonarQube e adicione no .env:
SONAR_TOKEN=seu_token_do_sonarqubeA análise é executada pela máquina local, não dentro do container do SonarQube.
dotnet tool install --global dotnet-sonarscannerO projeto usa o script scripts/sonar.sh para evitar repetir manualmente o fluxo begin, build, test e end do scanner .NET.
./scripts/sonar.shAo final, o relatório fica em http://localhost:9000/dashboard?id=BlogSharp.
Eu optei por respostas com apenas os campos necessários para cada endpoint. No caso de PostagemResponse, o PDF pede que a postagem esteja vinculada a usuário e tema, mas não exige retornar os objetos completos. Por isso, a resposta retorna UsuarioId e TemaId, sem expandir UsuarioResponse ou TemaResponse.
Essa escolha evita carregar e trafegar dados que o contrato não pediu.
Eu usei Data Annotations para validações simples, como campos obrigatórios, email e tamanho de texto. Isso deixa regras básicas visíveis nos DTOs e models sem criar uma camada extra só para validações simples.
Services e repositories usam Task e o sufixo Async porque acessam o banco com Entity Framework Core. Como acesso ao banco é uma operação de I/O, o async/await evita bloquear a thread do ASP.NET Core enquanto o PostgreSQL responde.
O PDF define autenticação e controle por tipo de usuário, mas não detalha todas as regras finas. Eu defini regras explícitas para evitar comportamento ambíguo:
- cadastro público sempre cria usuário comum;
- usuário pode atualizar e excluir o próprio cadastro;
- administrador pode excluir usuários;
- alteração de privilégio fica em rota administrativa separada;
- dono pode criar e atualizar a própria postagem;
- administrador pode excluir postagens para moderação;
- temas são administrados apenas por administradores.
O PDF sugere OpenAI API, Gemini API ou Azure AI Services. Eu usei OpenRouter com um modelo da OpenAI por uma questão prática: durante os testes, o Gemini retornou erro 403 de chave inválida mesmo com contas e chaves diferentes; a API direta da OpenAI exige créditos pagos; e o Azure ficou burocrático demais para este escopo, porque exige várias etapas de cadastro e configuração só para conseguir uma chave de API.
Com OpenRouter, o projeto mantém o objetivo do desafio: consumir uma API externa de IA, tratar resposta JSON e enriquecer postagens com resumo, tags e categoria. Também deixei IA_ENABLED=false por padrão para a API continuar funcionando mesmo sem chave de IA configurada.
Chave JWT, token do SonarQube e chave da IA ficam no .env local. O repositório versiona apenas .env.example, sem valores sensíveis.
O PDF pede SonarQube, integração com build, métricas e relatórios, mas não define meta mínima de cobertura nem exige aprovação obrigatória no Quality Gate padrão. Por isso, eu uso o SonarQube como ferramenta de inspeção: corrigir bugs, vulnerabilidades, hotspots relevantes e más práticas, sem criar testes artificiais apenas para subir porcentagem.
O SonarQube aponta hotspot no Dockerfile porque a imagem base do SDK .NET pode executar como root quando usada isoladamente.
No fluxo atual, considerei esse risco aceitável para ambiente local porque a API roda pelo Docker Compose com o usuário do host:
user: "${HOST_UID:-1000}:${HOST_GID:-1000}"Se o projeto ganhar um Dockerfile de produção, a imagem deve definir um usuário não-root diretamente.
Este projeto está licenciado conforme o arquivo LICENSE.