<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ricardotenv]]></title><description><![CDATA[Sou apaixonado por tecnologia, mergulhado no conhecimento livre. Compartilho insights sobre tecnologia e produtividade.]]></description><link>https://blog.ricardotenv.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 21:01:43 GMT</lastBuildDate><atom:link href="https://blog.ricardotenv.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Como fazer seeding no Django e popular seu banco de dados com Signals]]></title><description><![CDATA[Em projetos Django, frequentemente nos deparamos com a necessidade de pré-popular nosso banco de dados com informações pré-definidas. É aqui que entram os "seeds" - uma técnica para inserir dados iniciais de forma automatizada. Vamos explorar como im...]]></description><link>https://blog.ricardotenv.dev/como-fazer-seeding-no-django-e-popular-seu-banco-de-dados-com-signals</link><guid isPermaLink="true">https://blog.ricardotenv.dev/como-fazer-seeding-no-django-e-popular-seu-banco-de-dados-com-signals</guid><category><![CDATA[Python]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[Django]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Mon, 02 Jun 2025 13:11:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748711085757/1372cd72-6aa2-4168-8e75-52d5e3d30da3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Em <strong>projetos Django</strong>, frequentemente nos deparamos com a <strong>necessidade de pré-popular nosso banco de dados com informações pré-definidas</strong>. É aqui que entram os <strong>"seeds"</strong> - uma técnica para inserir dados iniciais de forma automatizada. Vamos explorar como implementar <strong>seeds no Django</strong> usando um exemplo prático e utilizando os <strong>signals</strong>.</p>
<h2 id="heading-o-problema">O problema</h2>
<p>Imagine que você está desenvolvendo uma aplicação que precisa de categorias predefinidas. Criar essas categorias manualmente toda vez que você configura um novo ambiente pode ser tedioso e propenso a erros.</p>
<h2 id="heading-a-solucao">A solução</h2>
<p>Django nos oferece uma maneira elegante de resolver isso usando <strong>signals</strong>, especificamente o signail <strong>post_migrate</strong>. Este signal é disparado após todas as <strong>migrações</strong> serem aplicadas, tornando-o o momento perfeito para inserir nossos dados iniciais.</p>
<p>Veja como podemos implementar um seed para uma tabela categorias e como é simples:</p>
<pre><code class="lang-python"><span class="hljs-comment"># meu_app/signals.py</span>
<span class="hljs-keyword">from</span> django.db.models.signals <span class="hljs-keyword">import</span> post_migrate
<span class="hljs-keyword">from</span> django.dispatch <span class="hljs-keyword">import</span> receiver
<span class="hljs-keyword">from</span> django.apps <span class="hljs-keyword">import</span> apps

<span class="hljs-meta">@receiver(post_migrate)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_default_categories</span>(<span class="hljs-params">sender, **kwargs</span>):</span>
    Categoria = apps.get_model(<span class="hljs-string">'meu_app'</span>, <span class="hljs-string">'Category'</span>)
    categories = [
        {<span class="hljs-string">"nome"</span>: <span class="hljs-string">"Tecnologia"</span>, <span class="hljs-string">"descricao"</span>: <span class="hljs-string">"Artigos e notícias sobre tecnologia."</span>},
        {<span class="hljs-string">"nome"</span>: <span class="hljs-string">"Esportes"</span>, <span class="hljs-string">"descricao"</span>: <span class="hljs-string">"Novidades do mundo dos esportes."</span>},
        {<span class="hljs-string">"nome"</span>: <span class="hljs-string">"Culinária"</span>, <span class="hljs-string">"descricao"</span>: <span class="hljs-string">"Receitas e dicas de culinária."</span>},
        {<span class="hljs-string">"nome"</span>: <span class="hljs-string">"Viagens"</span>, <span class="hljs-string">"descricao"</span>: <span class="hljs-string">"Destinos e dicas para viajantes."</span>},
    ]
    <span class="hljs-keyword">for</span> cat_data <span class="hljs-keyword">in</span> categories:
        category, created = Category.objects.get_or_create(
            name=cat_data[<span class="hljs-string">"nome"</span>],
            defaults={<span class="hljs-string">"description"</span>: cat_data.get(<span class="hljs-string">"descricao"</span>, <span class="hljs-string">""</span>)}
        )
        <span class="hljs-keyword">if</span> created:
            print(<span class="hljs-string">f"Categoria '<span class="hljs-subst">{category.name}</span>' criada."</span>)
        <span class="hljs-keyword">else</span>:
            print(<span class="hljs-string">f"Categoria '<span class="hljs-subst">{category.name}</span>' já existe."</span>)
    print(<span class="hljs-string">"Criação de categorias padrão concluída."</span>)
</code></pre>
<p>Este código define uma função que será executada após as migrações, ou seja, após o <strong>comando migrate</strong>. Dentro dela, definimos uma lista de categorias que queremos criar.</p>
<p>Não esquece de garantir que o Django saiba sobre nosso arquivo <code>signals.py</code>. A maneira recomendade de registrar signals é no método <code>ready()</code> da configuração do seu aplicativo <code>AppConfig</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># meu_app/apps.py</span>
<span class="hljs-keyword">from</span> django.apps <span class="hljs-keyword">import</span> AppConfig

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MeuAppConfig</span>(<span class="hljs-params">AppConfig</span>):</span>
    default_auto_field = <span class="hljs-string">'django.db.models.BigAutoField'</span>
    name = <span class="hljs-string">'meu_app'</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ready</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-comment"># Importar os sinais aqui para que sejam registrados quando o app estiver pronto</span>
        <span class="hljs-keyword">import</span> meu_app.signals
</code></pre>
<h3 id="heading-beneficios">Benefícios:</h3>
<ol>
<li><p><strong>Consistência</strong>: Garante que todos os ambientes (desenvolvimento, teste, produção) tenham os mesmos dados iniciais.</p>
</li>
<li><p><strong>Automação</strong>: Elimina a necessidade de inserção manual de dados.</p>
</li>
<li><p><strong>Versionamento</strong>: As seeds podem ser versionadas junto com o código, facilitando o rastreamento de mudanças.</p>
</li>
</ol>
<h3 id="heading-melhores-praticas">Melhores práticas:</h3>
<ol>
<li><p>Use <em>get_or_create()</em> para evitar duplicatas.</p>
</li>
<li><p>Mantenha suas seeds em um arquivo separado para melhor organização.</p>
</li>
<li><p>Considere usar fixtures para dados mais complexos.</p>
</li>
</ol>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Implementar seeds no Django é uma prática poderosa que pode economizar tempo e garantir consistência em seus projetos. Ao automatizar a criação de dados iniciais, você simplifica o processo de configuração e manutenção de sua aplicação Django. Existe outras formas de fazer seeds no Django, se você conhece comenta aqui em baixo quais são e não esquece de ir ler o meu post sobre o <a target="_blank" href="https://blog.ricardotenv.dev/o-poder-de-uma-boa-documentacao-em-python">poder de uma boa documentação em Python</a> para melhorar as suas aplicações no Django.</p>
]]></content:encoded></item><item><title><![CDATA[O poder de uma boa documentação em Python]]></title><description><![CDATA[Acho difícil alguém discordar o quão delicioso é para um desenvolvedor quando uma ferramenta necessária é devidamente documentada. E é ainda mais difícil alguém discordar da importância da documentação no desenvolvimento de API's, bibliotecas ou fram...]]></description><link>https://blog.ricardotenv.dev/o-poder-de-uma-boa-documentacao-em-python</link><guid isPermaLink="true">https://blog.ricardotenv.dev/o-poder-de-uma-boa-documentacao-em-python</guid><category><![CDATA[Python]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[docs]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Wed, 28 May 2025 13:06:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/59lC6TgZAbQ/upload/247261f70bbdcf43870b142638a33819.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Acho difícil alguém discordar o quão delicioso é para um desenvolvedor quando uma ferramenta necessária é <strong>devidamente documentada</strong>. E é ainda mais difícil alguém discordar da importância da <strong>documentação no desenvolvimento de API's</strong>, bibliotecas ou frameworks. <strong>A documentação é uma parte crucial do processo de desenvolvimento de software</strong>; uma ferramenta mal documentada pode não atrair desenvolvedores entusiastas e isso pode impactar diretamente no sucesso do seu projeto. Agora que chegamos a um acordo, aposto que você está pensando: <em>"Eu comento todo o meu código, então eu não preciso ler esse artigo sobre documentação"</em>. E é por isso que eu preciso explicar:</p>
<h2 id="heading-a-diferenca-entre-documentacao-e-comentarios"><strong>A diferença entre documentação e comentários</strong></h2>
<p>Uma documentação é a <em>receita de bolo</em> do seu projeto. Ela diz para o usuário como usar a sua ferramenta sem que seja necessário acessar e entender o seu código. Comentários, por outro lado, são notas para você e outros desenvolvedores que estarão olhando o código-fonte. Eles explicam o <em>porquê</em> de uma determinada lógica, um workaround específico, ou marcam TODOs.</p>
<p><strong>A documentação é voltada para o <em>usuário</em> da sua biblioteca, API ou framework. Os comentários são para os <em>mantenedores</em> e <em>contribuidores</em> do código.</strong></p>
<h2 id="heading-documentando-no-python"><strong>Documentando no Python</strong></h2>
<p>Agora que você já entendeu que <strong>documentar é uma parte importante do processo</strong>. E você que é menos experiente deve estar se perguntando, como fazer isso e <strong>o custo de uma boa documentação</strong>. Eu trago boas notícias, <strong>uma documentação pode ser gerada automaticamente</strong>, e neste artigo vou te mostrar como e até o final da leitura você será um expert.</p>
<p>Antes de qualquer coisa eu gostaria de explicar o porquê da minha escolha por Python, já que a maioria das linguagens possui ferramentas para gerar documentações. A resposta é bem simples, primeiro, é a linguagem que eu mais tenho domínio, e fica mais fácil para mim abordar esse tema considerado tão importante, segundo, é uma das melhores linguagens quando se trata de documentação.</p>
<h3 id="heading-docstrings"><strong>Docstrings</strong></h3>
<p>O coração da documentação em Python são as <em>docstrings</em>. Podem ser encontradas na <a target="_blank" href="https://www.python.org/dev/peps/pep-0257/">PEP-257 Docstring Conventions</a> ou na documentação do Python em <a target="_blank" href="https://www.google.com/search?q=https://docs.python.org/3/tutorial/controlflow.html%23documentation-strings">4.8.7. Documentation Strings</a>. Resumindo a documentação, uma docstring é uma string literal que precede a primeira declaração em um módulo, função, classe ou método, tornando-se o atributo especial <code>__doc__</code> (ou como chamamos <em>dunder</em> <code>__doc__</code>) desse objeto.</p>
<p>Existem dois formatos de docstrings:</p>
<h4 id="heading-one-line-vs-multi-line-docstrings"><strong>One-line vs Multi-line docstrings</strong></h4>
<p>O primeiro caso é usado para descrições mais óbvias e resumos e tudo deve estar na mesma linha, a abertura e fechamento de aspas triplas e o texto. Veja esse exemplo tirado da própria documentação:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">kos_root</span>():</span>
    <span class="hljs-string">"""Return the pathname of the KOS root directory."""</span>
    <span class="hljs-keyword">global</span> kos_root
    <span class="hljs-comment"># ...</span>
</code></pre>
<p>O segundo caso deve-se quebrar uma linha após a abertura de aspas triplas e o texto pode ser mais elaborado tendo o fechamento das aspas triplas na próxima linha.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">complex</span>(<span class="hljs-params">real=<span class="hljs-number">0.0</span>, imag=<span class="hljs-number">0.0</span></span>):</span>
    <span class="hljs-string">"""Form a complex number.

    Keyword arguments:
    real -- the real part (default 0.0)
    imag -- the imaginary part (default 0.0)
    """</span>
    <span class="hljs-keyword">if</span> real == <span class="hljs-number">0.0</span> <span class="hljs-keyword">and</span> imag == <span class="hljs-number">0.0</span>:
        <span class="hljs-keyword">return</span> complex_zero
    <span class="hljs-comment"># ...</span>
</code></pre>
<p>Agora que você já entendeu como funciona, vamos ver como a mágica acontece.</p>
<h2 id="heading-gerando-documentacao-com-python"><strong>Gerando documentação com Python</strong></h2>
<p>Eu vou criar um exemplo de uma biblioteca de código de um mensageiro fictício e vamos <strong>gerar a documentação desse exemplo com Sphinx</strong>. Mas antes vamos entender as:</p>
<h3 id="heading-convencoes-de-documentacao-e-formatadores-de-docstrings"><strong>Convenções de documentação e formatadores de docstrings</strong></h3>
<p>Não existem muitos critérios e nem regras para a escolha da convenção ou o formatador, fica por sua escolha mas é importante que você escolha um formato que seja compatível com a ferramenta que irá gerar o site da sua documentação. Segue uma lista com os principais formatadores e todos são compatíveis com <strong>Sphinx</strong>:</p>
<ul>
<li><p><a target="_blank" href="https://numpydoc.readthedocs.io/en/latest/format.html"><strong>Numpydoc</strong></a></p>
</li>
<li><p><a target="_blank" href="https://docutils.sourceforge.io/rst.html"><strong>reStructured Text</strong></a></p>
</li>
<li><p><a target="_blank" href="https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html"><strong>Google Docstrings</strong></a></p>
</li>
<li><p><a target="_blank" href="https://epydoc.sourceforge.net/epytext.html"><strong>Epytext</strong></a></p>
</li>
</ul>
<p>Eu deixei o link para você estudar essas convenções no futuro, mas por enquanto não precisa focar nisso, todas são muito parecidas, com algumas pequenas diferenças. Nesse exemplo usaremos o <strong>reStructured Text</strong> pois é o padrão do Sphinx e é muito parecido com Markdown.</p>
<h3 id="heading-gerando-uma-documentacao-com-sphinx"><strong>Gerando uma documentação com Sphinx</strong></h3>
<p>Como mencionei anteriormente, vou criar um exemplo de uma biblioteca de código de um mensageiro fictício e vamos gerar a documentação desse exemplo com Sphinx. Mas antes de botar a mão na massa, vamos entender o que é o Sphinx e por que ele é tão popular na comunidade Python.</p>
<p><strong>O que é o Sphinx?</strong></p>
<p>Sphinx é uma ferramenta poderosa que transforma arquivos de texto simples em diversos formatos de saída, como HTML, PDF, ePub e mais. Ele foi originalmente criado para a documentação da linguagem Python e, desde então, tornou-se a escolha padrão para muitos projetos Python e até mesmo para projetos em outras linguagens. Sua principal força reside na capacidade de processar reStructuredText (e Markdown, com extensões), uma linguagem de marcação fácil de ler, e integrá-lo perfeitamente com o código Python para extrair docstrings automaticamente.</p>
<p><strong>Por que escolher o Sphinx?</strong></p>
<ul>
<li><p><strong>Automação:</strong> Ele pode extrair automaticamente a documentação das docstrings do seu código Python usando a extensão <code>autodoc</code>. Isso significa que sua documentação e seu código permanecem sincronizados com mais facilidade.</p>
</li>
<li><p><strong>Formatos de saída múltiplos:</strong> Precisa de um site HTML? Um PDF para impressão? O Sphinx cuida disso.</p>
</li>
<li><p><strong>Referências cruzadas extensivas:</strong> Crie links facilmente entre diferentes partes da sua documentação, módulos, classes e funções.</p>
</li>
<li><p><strong>Temas e extensibilidade:</strong> Customize a aparência da sua documentação com temas (como o popular <code>sphinx_rtd_theme</code> usado pelo Read the Docs) e adicione funcionalidades com uma vasta gama de extensões.</p>
</li>
<li><p><strong>Suporte a reStructuredText e Markdown:</strong> Embora o reStructuredText seja o padrão e ofereça mais funcionalidades, o Sphinx também pode lidar com Markdown através de extensões, oferecendo flexibilidade.</p>
</li>
<li><p><strong>Ampla adoção:</strong> Muitos projetos grandes utilizam o Sphinx, o que significa uma comunidade grande, muitos recursos e tutoriais disponíveis.</p>
</li>
</ul>
<p><strong>Mãos à obra: configurando o Sphinx</strong></p>
<ol>
<li><p><strong>Instalação:</strong></p>
<p> Primeiro, você precisará instalar o Sphinx. Recomendo também instalar o <code>sphinx_rtd_theme</code> para um visual moderno:</p>
<pre><code class="lang-bash"> pip install sphinx sphinx-rtd-theme
</code></pre>
</li>
<li><p><strong>Iniciando o projeto de documentação:</strong></p>
<p> Navegue até o diretório raiz do seu projeto Python e execute o assistente de configuração do Sphinx:</p>
<pre><code class="lang-bash"> sphinx-quickstart
</code></pre>
<p> Este comando fará uma série de perguntas para configurar seu projeto de documentação. Algumas das opções importantes:</p>
<ul>
<li><p><strong>Nome do projeto:</strong> O nome da sua biblioteca ou aplicação.</p>
</li>
<li><p><strong>Nome do autor:</strong> Seu nome ou o nome da sua organização.</p>
</li>
<li><p><strong>Versão do projeto:</strong> A versão atual do seu projeto.</p>
</li>
<li><p><strong>Extensões:</strong> Aqui é crucial habilitar algumas extensões. Pressione 'y' para:</p>
<ul>
<li><p><code>sphinx.ext.autodoc</code>: Para incluir documentação de docstrings.</p>
</li>
<li><p><code>sphinx.ext.napoleon</code>: Se você planeja usar docstrings no estilo Google ou NumPy.</p>
</li>
<li><p><code>sphinx.ext.viewcode</code>: Para adicionar links para o código fonte.</p>
</li>
<li><p><code>sphinx.ext.githubpages</code>: Se você planeja hospedar no GitHub Pages.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>    Após responder a todas as perguntas, o <code>sphinx-quickstart</code> criará um diretório <code>docs</code> (ou o nome que você escolheu) com vários arquivos, incluindo:</p>
<ul>
<li><p><code>conf.py</code>: O arquivo de configuração principal do Sphinx. É aqui que você define o tema, ativa extensões, e mais importante, informa ao Sphinx onde encontrar seu código Python.</p>
</li>
<li><p><code>index.rst</code>: A página inicial da sua documentação.</p>
</li>
<li><p><code>Makefile</code> (ou <code>make.bat</code> no Windows): Arquivos de utilidade para construir sua documentação.</p>
</li>
</ul>
<ol start="3">
<li><p><strong>Configurando o conf.py:</strong></p>
<p> Abra o arquivo docs/conf.py e faça algumas edições essenciais:</p>
<ul>
<li><p><strong>Descomente e ajuste o</strong> <code>sys.path</code>: Para que o <code>autodoc</code> encontre seus módulos Python, você precisa adicionar o diretório do seu código ao <code>sys.path</code>. Se sua estrutura de projeto for algo como:</p>
<pre><code class="lang-python">  meu_projeto/
  ├── docs/
  │   └── conf.py
  └── src/
      └── meu_mensageiro/
          └── __init__.py
          └── core.py
</code></pre>
<p>  Você adicionaria:</p>
<pre><code class="lang-python">  <span class="hljs-keyword">import</span> os
  <span class="hljs-keyword">import</span> sys
  sys.path.insert(<span class="hljs-number">0</span>, os.path.abspath(<span class="hljs-string">'../src'</span>)) <span class="hljs-comment"># Ajuste conforme sua estrutura</span>
</code></pre>
</li>
<li><p><strong>Defina o tema HTML:</strong> Para usar o tema Read the Docs:</p>
<pre><code class="lang-python">  html_theme = <span class="hljs-string">'sphinx_rtd_theme'</span>
</code></pre>
</li>
<li><p><strong>Verifique as extensões:</strong> Garanta que <code>sphinx.ext.autodoc</code> está na lista <code>extensions</code>.</p>
</li>
</ul>
</li>
</ol>
<p><strong>Exemplo prático: Documentando nossa biblioteca de mensageiro fictício</strong></p>
<p>Vamos supor que temos um módulo <code>core.py</code> dentro de <code>src/meu_mensageiro/</code> com o seguinte conteúdo:</p>
<pre><code class="lang-python"><span class="hljs-comment"># src/meu_mensageiro/core.py</span>

<span class="hljs-string">"""
Módulo principal do Mensageiro Fictício.

Este módulo contém as funcionalidades centrais para enviar e receber mensagens.
"""</span>

MAX_MESSAGE_LENGTH = <span class="hljs-number">1024</span>
<span class="hljs-string">"""Constante que define o tamanho máximo de uma mensagem."""</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span>:</span>
    <span class="hljs-string">"""
    Representa uma mensagem no sistema.

    :param sender: O remetente da mensagem.
    :type sender: str
    :param content: O conteúdo da mensagem.
    :type content: str
    :raises ValueError: Se o conteúdo da mensagem exceder `MAX_MESSAGE_LENGTH`.
    """</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, sender: str, content: str</span>):</span>
        <span class="hljs-keyword">if</span> len(content) &gt; MAX_MESSAGE_LENGTH:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Conteúdo da mensagem muito longo."</span>)
        self.sender = sender
        self.content = content
        self.is_sent = <span class="hljs-literal">False</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self</span>) -&gt; bool:</span>
        <span class="hljs-string">"""
        Envia a mensagem.

        Simula o envio de uma mensagem. Em um cenário real, isso se conectaria
        a um serviço de mensageria.

        :return: True se a mensagem foi enviada com sucesso, False caso contrário.
        :rtype: bool
        """</span>
        print(<span class="hljs-string">f"Mensagem de <span class="hljs-subst">{self.sender}</span> enviada: <span class="hljs-subst">{self.content}</span>"</span>)
        self.is_sent = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">receive_message</span>(<span class="hljs-params">user: str</span>) -&gt; Message | <span class="hljs-keyword">None</span>:</span>
    <span class="hljs-string">"""
    Simula o recebimento de uma nova mensagem para um usuário.

    :param user: O nome do usuário para verificar mensagens.
    :type user: str
    :return: Um objeto :class:`Message` se houver uma nova mensagem, ou None caso contrário.
    :rtype: Message or None
    """</span>
    <span class="hljs-comment"># Em um sistema real, haveria uma lógica para buscar mensagens</span>
    <span class="hljs-keyword">if</span> user == <span class="hljs-string">"ricardo"</span>:
        <span class="hljs-keyword">return</span> Message(<span class="hljs-string">"servidor_central"</span>, <span class="hljs-string">"Bem-vindo ao Mensageiro Fictício!"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>Neste exemplo, usamos o formato reStructuredText para as docstrings, como discutido. Observe como descrevemos parâmetros (<code>:param:</code>), tipos (<code>:type:</code>), o que é retornado (<code>:return:</code>, <code>:rtype:</code>) e exceções (<code>:raises:</code>). Informações detalhadas sobre como escrever docstrings podem ser encontradas em tutoriais como o do <a target="_blank" href="https://www.datacamp.com/tutorial/docstrings-python">DataCamp sobre docstrings em Python</a>.</p>
<ol start="4">
<li><p>Criando arquivos .rst para o autodoc:</p>
<p> Agora, vamos dizer ao Sphinx para gerar documentação para o nosso módulo core. Crie um arquivo chamado meu_mensageiro.rst dentro do diretório docs/ com o seguinte conteúdo:</p>
<pre><code class="lang-plaintext"> .. automodule:: meu_mensageiro.core
    :members:
    :undoc-members:
    :show-inheritance:
</code></pre>
<ul>
<li><p><code>.. automodule:: meu_mensageiro.core</code>: Diz ao Sphinx para documentar o módulo <code>meu_mensageiro.core</code>.</p>
</li>
<li><p><code>:members:</code>: Inclui todos os membros públicos (funções, classes, variáveis) do módulo.</p>
</li>
<li><p><code>:undoc-members:</code>: Inclui membros que não têm docstrings (use com cautela, o ideal é documentar tudo).</p>
</li>
<li><p><code>:show-inheritance:</code>: Mostra as classes base para as classes documentadas.</p>
</li>
</ul>
</li>
</ol>
<p>    Agora, adicione este novo arquivo ao seu <code>toctree</code> principal no arquivo <code>docs/index.rst</code>:</p>
<pre><code class="lang-plaintext">    .. toctree::
       :maxdepth: 2
       :caption: Conteúdo:

       meu_mensageiro
</code></pre>
<ol start="5">
<li><p>Gerando a documentação:</p>
<p> Volte para o diretório docs/ no seu terminal e execute:</p>
<pre><code class="lang-bash"> make html
</code></pre>
<p> Se tudo estiver configurado corretamente, o Sphinx processará seus arquivos <code>.rst</code> e as docstrings do seu código, gerando a documentação em HTML no diretório <code>docs/_build/html/</code>. Abra o arquivo <code>index.html</code> nesse diretório para ver sua documentação em ação!</p>
<p> Você verá uma página bem formatada com a descrição do seu módulo, a constante <code>MAX_MESSAGE_LENGTH</code>, a classe <code>Message</code> com seus métodos, e a função <code>receive_message</code>, tudo extraído das docstrings que você escreveu.</p>
</li>
</ol>
<p><strong>Dicas para uma documentação de qualidade com Sphinx:</strong></p>
<ul>
<li><p><strong>Seja consistente:</strong> Escolha um estilo de docstring (reStructuredText, Google, NumPy) e mantenha-o em todo o projeto. A consistência é fundamental para a legibilidade.</p>
</li>
<li><p><strong>Documente a API pública:</strong> Foque em documentar a interface que outros desenvolvedores (ou você mesmo no futuro) usarão. Uma boa documentação de API é crucial.</p>
</li>
<li><p><strong>Inclua exemplos de uso:</strong> Docstrings são um ótimo lugar para pequenos exemplos. Para exemplos mais complexos, considere criar seções separadas na sua documentação ou até mesmo um diretório <code>examples/</code>.</p>
</li>
<li><p><strong>Mantenha atualizado:</strong> Documentação desatualizada é pior do que nenhuma documentação. Faça da atualização da documentação parte do seu processo de desenvolvimento.</p>
</li>
<li><p><strong>Use referências cruzadas:</strong> Sphinx facilita a criação de links para outras partes da sua documentação usando <em>roles</em> como <code>:mod:</code>, <code>:func:</code>, <code>:class:</code>. Isso torna a navegação muito mais fluida.</p>
</li>
<li><p><strong>Explore extensões:</strong> O ecossistema de extensões do Sphinx é vasto. Precisa de diagramas? Suporte a Jupyter Notebooks? Provavelmente existe uma extensão para isso.</p>
</li>
</ul>
<p>A documentação é uma parte vital de qualquer projeto de software, grande ou pequeno. Mesmo que você esteja trabalhando em projetos mais simples, como explorar a criação de <a target="_blank" href="https://blog.ricardotenv.dev/como-criar-um-site-com-python-puro-sem-framework-usando-cgi">um site com Python puro, sem framework (usando CGI)</a>, entender como articular a funcionalidade do seu código é uma habilidade valiosa que se reflete na qualidade da sua documentação.</p>
<h2 id="heading-conclusao"><strong>Conclusão</strong></h2>
<p>Dominar ferramentas como o Sphinx e adotar boas práticas de escrita de docstrings pode parecer um esforço extra inicialmente, mas os benefícios a longo prazo são imensos. Uma documentação clara, concisa e abrangente economiza tempo, reduz a frustração, facilita a colaboração e, em última análise, contribui significativamente para o sucesso e a adoção do seu projeto Python.</p>
<p>Lembre-se: um código bem documentado não é apenas um presente para os outros, é um presente para o seu eu futuro. Invista tempo em documentar seu trabalho, e você colherá os frutos.</p>
<p>Espero que este guia tenha convencido você do poder de uma boa documentação em Python e o tenha preparado para começar a documentar seus próprios projetos como um verdadeiro expert!</p>
]]></content:encoded></item><item><title><![CDATA[O jeito certo de integrar o FastAPI com a OpenAI]]></title><description><![CDATA[Introdução
O FastAPI é uma ferramenta moderna e super eficiente, com uma comunidade que não para de crescer, conquistou seu espaço, principalmente em projetos que envolvem IA. A performance e a facilidade de uso são incríveis!
Mas, como nem tudo são ...]]></description><link>https://blog.ricardotenv.dev/o-jeito-certo-de-integrar-o-fastapi-com-a-openai</link><guid isPermaLink="true">https://blog.ricardotenv.dev/o-jeito-certo-de-integrar-o-fastapi-com-a-openai</guid><category><![CDATA[openai]]></category><category><![CDATA[FastAPI]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Fri, 18 Apr 2025 23:10:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745017738623/996e2933-7402-47c7-998f-a236067d04ee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introducao">Introdução</h2>
<p>O <strong>FastAPI</strong> é uma ferramenta moderna e super eficiente, com uma comunidade que não para de crescer, conquistou seu espaço, principalmente em projetos que envolvem <strong>IA</strong>. A <strong>performance</strong> e a <strong>facilidade de uso</strong> são incríveis!</p>
<p>Mas, como nem tudo são flores, esse sucesso todo traz alguns desafios. Com muitas empresas começando a usar, algumas implementações podem acabar tropeçando em armadilhas comuns, simplesmente pela falta de experiência dos desenvolvedores com a ferramenta ou por seguirmos o caminho que <em>parece</em> mais fácil de início.</p>
<p>Esse post nasceu justamente de uma situação real que enfrentei num projeto e de uma <em>thread</em> bem interessante que acompanhei lá no <a target="_blank" href="https://github.com/fastapi/fastapi/discussions/9234">GitHub do FastAPI.</a> Vamos falar sobre como <strong>gerenciar o cliente da OpenAI</strong> (ou qualquer outro recurso similar) do jeito certo dentro do FastAPI usando o <strong>lifespan</strong>.</p>
<h2 id="heading-o-cenario">O cenário</h2>
<p>Imagine que estamos construindo o backend para um chat, tipo um <strong>ChatGPT</strong> mais simples. Poderia ser para uma interface customizada, para consumo sob demanda, ou até para usar modelos open-source compatíveis com <strong>a biblioteca openai do Python</strong>.</p>
<p>E para essa missão, claro, vamos usar o FastAPI, já que até as <a target="_blank" href="https://blog.ricardotenv.dev/renderizando-html-dinamico-com-fastapi-e-jinja2-uma-alternativa-simples-aos-spas">SPAs eu estou substituindo pelo FastAPI</a>.</p>
<p>Pra você que está lendo, não ficar perdido. Eu vou mostrar primeiro a forma como muita gente começa: o jeito simples, que <em>funciona</em> na hora, mas que esconde alguns riscos e pode trazer muita dor de cabeça lá na frente.</p>
<p>Depois, vamos mergulhar na solução <strong>usando o lifespan do FastAPI</strong>. O foco desse artigo é mostrar como usar o lifespan especificamente para integrar o cliente da biblioteca openai de forma correta. O lifespan tem mais utilidades, mas não vamos nos aprofundar em todas elas hoje, talvez em um post futuro!</p>
<h2 id="heading-o-jeito-ruim-de-fazer-instanciando-o-cliente-no-topo-do-arquivo">O jeito ruim de fazer: Instânciando o cliente no topo do arquivo</h2>
<p>Quando estamos começando um projeto, a intuição (e muitos exemplos por aí) nos leva a fazer algo assim: <strong>instanciar o cliente assíncrono da openai diretamente no escopo global</strong> do nosso arquivo principal. É simples, né?</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
<span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> AsyncOpenAI
<span class="hljs-keyword">import</span> os

client = AsyncOpenAI(api_key=os.getenv(<span class="hljs-string">"OPENAI_API_KEY"</span>))

app = FastAPI()

<span class="hljs-meta">@app.post("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">prompt: str</span>):</span>
    <span class="hljs-comment"># Usando o cliente global diretamente</span>
    response = <span class="hljs-keyword">await</span> client.chat.completions.create(
        model=<span class="hljs-string">"..."</span>
        messages=[{<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt}]
    )
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"reply"</span>: response.choices[<span class="hljs-number">0</span>].message.content}
</code></pre>
<p>Bom, como eu disse, isso <em>funciona</em>. Você <strong>roda o Uvicorn</strong> e consegue fazer chamadas. Mas por baixo dos panos, vários problemas estão se acumulando:</p>
<ol>
<li><p><strong>Instanciação prematura:</strong> O cliente é criado <em>antes</em> mesmo do servidor FastAPI estar totalmente pronto para receber requisições. Se você precisasse carregar alguma configuração assíncrona <em>antes</em> de criar o cliente, já não daria certo.</p>
</li>
<li><p><strong>Gerenciamento de recursos falhos:</strong> Essa instância cliente fica "viva" durante todo o tempo que o processo do servidor rodar. Mais importante: ela <strong>não é fechada corretamente</strong> quando o servidor termina. O <strong>AsyncOpenAI</strong> usa o <strong>httpx.AsyncClient</strong> por baixo, que mantém <strong>um pool de conexões HTTP</strong>. Sem chamar <code>await client.aclose()</code>, essas conexões ficam abertas, vazando recursos (sockets, memória).</p>
</li>
<li><p><strong>Dificuldade em testes:</strong> Testar endpoints que dependem de estado global é um pé no saco. Mockar ou substituir essa instância cliente para testes unitários ou de integração fica bem mais complicado.</p>
</li>
<li><p><strong>Replicação por worker:</strong> Se você <strong>rodar sua aplicação com múltiplos workers</strong> (como o Gunicorn faz em produção), <em>cada worker</em> vai criar sua própria instância global do cliente, multiplicando o <strong>desperdício de recursos</strong> e os <strong>problemas de conexão</strong>.</p>
</li>
</ol>
<h2 id="heading-o-jeito-certo-usando-o-lifespan-do-fastapi">“O jeito certo”: Usando o lifespan do FastAPI</h2>
<p>Eu coloquei entre aspas pois existem outras formas de fazer, como o <em>before</em> ou <em>on_event</em> e outras formas que também pode funcionar e ao mesmo tempo seguir as boas práticas. Cagar regra na nossa área definitivamente não é uma boa prática.</p>
<p>Voltando ao assunto. Felizmente, o FastAPI oferece uma solução elegante para <strong>gerenciar o ciclo de vida de recursos</strong> como nosso cliente openai: <strong>o parâmetro lifespan</strong>.</p>
<p>Ele utiliza um <strong>async context manager</strong> (gerenciador de contexto assíncrono) para garantir que seu recurso seja inicializado <em>depois</em> que a aplicação começa e finalizado <em>antes</em> que ela termine completamente.</p>
<p>Veja como fica bem mais interessante:</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> openai <span class="hljs-keyword">import</span> AsyncOpenAI
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request
<span class="hljs-keyword">from</span> contextlib <span class="hljs-keyword">import</span> asynccontextmanager

<span class="hljs-comment"># O gerenciador de contexto para o ciclo de vida</span>
<span class="hljs-meta">@asynccontextmanager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lifespan</span>(<span class="hljs-params">app: FastAPI</span>):</span>
    <span class="hljs-comment"># Setup: Roda antes da aplicação iniciar</span>
    print(<span class="hljs-string">"Iniciando cliente OpenAI..."</span>)
    app.state.openai_client = AsyncOpenAI(api_key=os.getenv(<span class="hljs-string">"OPENAI_API_KEY"</span>))
    print(<span class="hljs-string">"Cliente OpenAI pronto!"</span>)

    <span class="hljs-keyword">yield</span> <span class="hljs-comment"># Essa função congela nesse momento e aplicação entra em ação</span>

    <span class="hljs-comment"># Teardown: A mágica acontece aqui, depois que voc6e para a aplicação esse código é executado</span>
    print(<span class="hljs-string">"Fechando cliente OpenAI..."</span>)
    <span class="hljs-keyword">await</span> app.state.openai_client.close()
    print(<span class="hljs-string">"Cliente OpenAI fechado."</span>)

app = FastAPI(lifespan=lifespan)

<span class="hljs-meta">@app.post("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">request: Request, prompt: str</span>):</span> <span class="hljs-comment"># Simplificado para receber só o prompt</span>
    <span class="hljs-comment"># Acessa o cliente via app.state</span>
    client: AsyncOpenAI = request.app.state.openai_client

    response = <span class="hljs-keyword">await</span> client.chat.completions.create(c
        model=<span class="hljs-string">"..."</span>,
        messages=[{<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt}]
    )
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"reply"</span>: response.choices[<span class="hljs-number">0</span>].message.content}
</code></pre>
<p>Entendendo a mágica: <strong>asynccontextmanager</strong> e <strong>yield</strong></p>
<p>O decorador <code>@asynccontextmanager</code> (da biblioteca <strong>contextlib</strong> do Python) é a chave aqui. Ele transforma nossa função <code>lifespan</code> em um <strong>gerenciador de contexto assíncrono</strong> especial.</p>
<ol>
<li><p><strong>Antes do yield:</strong> Todo o código antes da linha <strong>yield</strong> é a fase de <strong>setup</strong>. O FastAPI executa isso durante a inicialização <em>(aqui eu imagino que você já entendeu o lance)</em>. É aqui que criamos nosso cliente <strong>AsyncOpenAI</strong> e o guardamos em <code>app.state.openai_client</code>. O <code>app.state</code> é um objeto tipo dicionário feito exatamente para <strong>guardar recursos que precisam viver junto com a aplicação</strong>.</p>
</li>
<li><p><strong>O yield:</strong> A palavra <strong><em>yield</em></strong> é o ponto de "pausa". A função <code>lifespan</code> fica congelada aqui, e o controle volta para o FastAPI, que finalmente começa a aceitar e processar requisições. Enquanto a aplicação está rodando, nossos endpoints (como <code>/chat</code>) podem acessar o cliente via <a target="_blank" href="http://request.app"><code>request.app</code></a><code>.state.openai_client</code>.</p>
</li>
<li><p><strong>Depois do yield:</strong> Quando o FastAPI recebe um sinal para desligar (por exemplo, quando você pressiona Ctrl+C no terminal), ele "descongela" a função <code>lifespan</code> <em>depois</em> do <code>yield</code>. Essa é a fase de <strong>teardown</strong>. Aqui, executamos a limpeza: <code>await client.close()</code>, <strong>garantindo que as conexões HTTP sejam fechadas corretamente</strong>.</p>
</li>
</ol>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Sim, instanciar o cliente globalmente <em>vai funcionar</em> no começo. O FastAPI é robusto e vai saber lidar com isso. Mas essa abordagem não escala bem e esconde problemas que vão te assombrar em produção ou em aplicações maiores.</p>
<p>E pra ser honesto, eu usei a openai para chamar a sua atenção mas a minha verdadeira intenção era te mostrar o uso do lifespan.</p>
]]></content:encoded></item><item><title><![CDATA[Renderizando HTML dinâmico com FastAPI e Jinja2: Uma alternativa simples aos SPAs]]></title><description><![CDATA[Introdução
No mundo do desenvolvimento web moderno, frameworks JavaScript como React, Vue e Angular (SPAs - Single Page Applications) dominam muitas discussões. Eles são incrivelmente poderosos, mas será que são sempre a ferramenta certa para todas a...]]></description><link>https://blog.ricardotenv.dev/renderizando-html-dinamico-com-fastapi-e-jinja2-uma-alternativa-simples-aos-spas</link><guid isPermaLink="true">https://blog.ricardotenv.dev/renderizando-html-dinamico-com-fastapi-e-jinja2-uma-alternativa-simples-aos-spas</guid><category><![CDATA[FastAPI]]></category><category><![CDATA[Jinja2]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Tue, 01 Apr 2025 17:56:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743530125338/458a710c-90d4-4b4c-89bb-369110d8d9c3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introducao">Introdução</h2>
<p>No mundo do desenvolvimento web moderno, frameworks JavaScript como React, Vue e Angular (SPAs - Single Page Applications) dominam muitas discussões. Eles são incrivelmente poderosos, mas será que são sempre a ferramenta certa para <em>todas</em> as tarefas? E se você precisar de uma página dinâmica, um dashboard interno, ou uma interface administrativa simples e não quiser lidar com a complexidade de um build frontend separado, gerenciamento de estado no cliente e APIs dedicadas apenas para buscar dados?</p>
<p><strong>FastAPI</strong>, conhecido por sua velocidade e facilidade na criação de APIs, oferece uma solução elegante: a renderização de templates HTML diretamente no servidor usando a popular engine <strong>Jinja2</strong>. Embora não seja seu foco principal (que é a criação de APIs), essa capacidade é extremamente útil e surpreendentemente simples de implementar.</p>
<p>Neste post, vamos explorar passo a passo como configurar o FastAPI para renderizar páginas HTML dinâmicas, passando dados do seu backend Python diretamente para o navegador. Vamos construir um exemplo prático que exibe informações da requisição recebida.</p>
<h2 id="heading-por-que-renderizar-html-no-servidor-com-fastapi"><strong>Por que renderizar HTML no servidor com FastAPI?</strong></h2>
<p>Antes de mergulhar no código, vamos entender por que essa abordagem pode ser vantajosa:</p>
<ol>
<li><p><strong>Simplicidade:</strong> Para muitos casos de uso (dashboards, páginas de conteúdo, formulários simples), gerenciar um projeto frontend completo é um exagero. Renderizar no servidor elimina a necessidade de um processo de build frontend, roteamento no cliente complexo e gerenciamento de estado separado.</p>
</li>
<li><p><strong>Velocidade de Desenvolvimento:</strong> Você pode prototipar e construir interfaces funcionais rapidamente usando apenas Python e HTML com Jinja2.</p>
</li>
<li><p><strong>Desempenho:</strong> Para a primeira carga, o HTML já chega renderizado ao navegador, o que pode ser percebido como mais rápido em algumas situações (Time to First Contentful Paint).</p>
</li>
<li><p><strong>SEO:</strong> Páginas renderizadas no servidor são geralmente mais fáceis para os motores de busca indexarem (embora o Google tenha melhorado muito na indexação de SPAs).</p>
</li>
<li><p><strong>Menos JavaScript:</strong> Reduz a quantidade de JavaScript que precisa ser baixado, parseado e executado no cliente.</p>
</li>
</ol>
<p>Claro, SPAs têm seu lugar, especialmente para aplicações altamente interativas e complexas. Mas para muitos cenários, a renderização server-side com <strong>FastAPI</strong> + <strong>Jinja2</strong> é uma alternativa pragmática e eficiente.</p>
<h2 id="heading-configuracao-inicial-do-projeto-fastapi"><strong>Configuração inicial do projeto FastAPI</strong></h2>
<p>Vamos começar com uma aplicação FastAPI mínima. Se você ainda não tem o FastAPI e o Uvicorn (servidor ASGI) instalados, instale-os:</p>
<p>É altamente recomendável usar um ambiente virtual para gerenciar as dependências do seu projeto:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Crie um ambiente virtual (se ainda não tiver)</span>
python -m venv .venv

<span class="hljs-comment"># Ative o ambiente (Linux/macOS)</span>
<span class="hljs-built_in">source</span> .venv/bin/activate
<span class="hljs-comment"># Ou (Windows - Command Prompt)</span>
<span class="hljs-comment"># .venv\Scripts\activate.bat</span>
<span class="hljs-comment"># Ou (Windows - PowerShell)</span>
<span class="hljs-comment"># .venv\Scripts\Activate.ps1</span>

<span class="hljs-comment"># Agora instale as bibliotecas dentro do ambiente ativado</span>
pip install fastapi uvicorn
</code></pre>
<p>Crie um arquivo chamado main.py:</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request

app = FastAPI()

<span class="hljs-meta">@app.get('/')</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-comment"># Nossa lógica de renderização virá aqui em breve</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"API está funcionando, mas ainda não renderiza HTML"</span>}

<span class="hljs-comment"># Para rodar: uvicorn main:app --reload</span>
</code></pre>
<p>Neste ponto, se você executar <code>uvicorn main:app --reload</code> e acessar http://127.0.0.1:8000/ no navegador, verá a resposta JSON.</p>
<h2 id="heading-instalando-e-configurando-jinja2"><strong>Instalando e configurando Jinja2</strong></h2>
<p>O FastAPI não inclui um motor de templates por padrão, mas se integra perfeitamente com o Jinja2. Vamos instalar a biblioteca:</p>
<pre><code class="lang-bash">pip install jinja2
</code></pre>
<p>Agora, precisamos informar ao FastAPI como usar o Jinja2. Modifique seu main.py:</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request
<span class="hljs-keyword">from</span> fastapi.templating <span class="hljs-keyword">import</span> Jinja2Templates

app = FastAPI()

<span class="hljs-comment"># Crie uma instância de Jinja2Templates, especificando o diretório dos templates</span>
templates = Jinja2Templates(directory=<span class="hljs-string">"templates"</span>)

<span class="hljs-meta">@app.get('/')</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-comment"># Em breve, substituiremos isso pela renderização do template</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Jinja2 configurado, mas ainda não usado"</span>}

<span class="hljs-comment"># Não se esqueça de criar um diretório chamado 'templates'</span>
<span class="hljs-comment"># na raiz do seu projeto!</span>
<span class="hljs-comment"># Estrutura do Projeto:</span>
<span class="hljs-comment"># .</span>
<span class="hljs-comment"># ├── main.py</span>
<span class="hljs-comment"># └── templates/</span>
<span class="hljs-comment">#     └── (Aqui ficarão nossos arquivos .html)</span>
</code></pre>
<p>Criamos uma instância templates da classe <strong>Jinja2Templates</strong>. O parâmetro directory="templates" diz ao Jinja onde procurar pelos nossos arquivos HTML. Certifique-se de criar essa pasta templates no mesmo nível do seu main.py.</p>
<h2 id="heading-criando-o-template-html-indexhtml"><strong>Criando o template HTML (index.html)</strong></h2>
<p>Dentro da pasta templates, crie um arquivo chamado index.html. Este será o nosso template base. Vamos usar a sintaxe do Jinja2 (<code>{{ variavel }}</code> e <code>{% logica %}</code>) para inserir dados dinamicamente. Para deixar visualmente mais agradável, incluiremos links para TailwindCSS e DaisyUI via CDN.</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- templates/index.html --&gt;</span>
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"pt-BR"</span> <span class="hljs-attr">data-theme</span>=<span class="hljs-string">cupcake</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Detalhes da Requisição - FastAPI<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mx-auto p-4 min-h-screen"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-6"</span>&gt;</span>Informações da Sua Requisição<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"overflow-x-auto shadow-md rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table table-zebra w-full"</span>&gt;</span>
                <span class="hljs-comment">&lt;!-- head --&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/3"</span>&gt;</span>Item<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Valor<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- Linhas da tabela serão preenchidas com dados do context --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>User Agent<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ user_agent }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>Seu IP (Host)<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ client_host }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>Método HTTP<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ request_method }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>URL Solicitada<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ request_url }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>Cookies Enviados<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 p-2 rounded text-sm whitespace-pre-wrap"</span>&gt;</span>{{ cookies | tojson(indent=2) }}<span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                     <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>Caminho da URL (via objeto request)<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ request.url.path }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span> {# Exemplo de acesso direto ao request no template #}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- Seção de Query Parameters --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold pt-4"</span> <span class="hljs-attr">colspan</span>=<span class="hljs-string">"2"</span>&gt;</span>Query Parameters:<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    {% if query_params %}
                        {% for key, value in query_params.items() %}
                        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-8"</span>&gt;</span>{{ key }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ value }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                        {% endfor %}
                    {% else %}
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-8 italic"</span> <span class="hljs-attr">colspan</span>=<span class="hljs-string">"2"</span>&gt;</span>Nenhum query parameter encontrado.<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    {% endif %}
                <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p><strong>Explicação do Template:</strong></p>
<ul>
<li><p><code>{{ variavel }}</code>: Renderiza o valor de uma variável passada no <em>contexto</em> (veremos isso a seguir). Ex: <code>{{ user_agent }}</code>.</p>
</li>
<li><p><code>{% if query_params %}</code> ... <code>{% else %}</code> ... <code>{% endif %}</code>: Bloco condicional. Exibe o conteúdo interno dependendo se query_params existe e não está vazio.</p>
</li>
<li><p><code>{% for key, value in query_params.items() %}</code> ... <code>{% endfor %}</code>: Loop que itera sobre os itens do dicionário query_params.</p>
</li>
<li><p><code>{{ cookies | tojson(indent=2) }}</code>: Usa um <em>filtro</em> Jinja (tojson) para formatar o dicionário de cookies como uma string JSON legível.</p>
</li>
<li><p><code>{{ request.url.path }}</code>: Mostra que, como passamos o objeto request no contexto, podemos acessar seus atributos diretamente no template.</p>
</li>
</ul>
<h2 id="heading-coletando-dados-e-renderizando-o-template-na-rota"><strong>Coletando dados e renderizando o template na rota</strong></h2>
<p>Agora, vamos modificar nossa rota root em main.py para coletar os dados da requisição e usar a instância templates para renderizar o index.html.</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request
<span class="hljs-keyword">from</span> fastapi.templating <span class="hljs-keyword">import</span> Jinja2Templates

app = FastAPI()

templates = Jinja2Templates(directory=<span class="hljs-string">"templates"</span>)

<span class="hljs-meta">@app.get('/')</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-comment"># 1. Coletar dados da requisição</span>
    user_agent = request.headers.get(<span class="hljs-string">'user-agent'</span>, <span class="hljs-string">'N/A'</span>)
    client_host = request.client.host <span class="hljs-keyword">if</span> request.client <span class="hljs-keyword">else</span> <span class="hljs-string">'N/A'</span>
    request_method = request.method
    request_url = str(request.url)
    cookies = request.cookies
    <span class="hljs-comment"># dict() converte o MultiDict para um dicionário simples</span>
    query_params = dict(request.query_params)

    <span class="hljs-comment"># 2. Criar o dicionário de contexto</span>
    <span class="hljs-comment"># Este dicionário passa os dados do Python para o Jinja2</span>
    context = {
        <span class="hljs-string">"request"</span>: request,
        <span class="hljs-string">"user_agent"</span>: user_agent,
        <span class="hljs-string">"client_host"</span>: client_host,
        <span class="hljs-string">"request_method"</span>: request_method,
        <span class="hljs-string">"request_url"</span>: request_url,
        <span class="hljs-string">"cookies"</span>: cookies,
        <span class="hljs-string">"query_params"</span>: query_params
    }

    <span class="hljs-comment"># 3. Renderizar o template com o contexto</span>
    <span class="hljs-keyword">return</span> templates.TemplateResponse(<span class="hljs-string">"index.html"</span>, context)

<span class="hljs-comment"># Para rodar: uvicorn main:app --reload</span>
</code></pre>
<p><strong>Explicação do Código da Rota:</strong></p>
<ol>
<li><p><strong>Coleta de Dados</strong>: Extraímos várias informações úteis do objeto request (tipo Request), como headers, IP do cliente, método, URL completa, cookies e query parameters.</p>
</li>
<li><p><strong>Context</strong>: Este é o <strong>ponto chave</strong>. O context é um dicionário Python que mapeia nomes de variáveis (chaves) para valores que você quer disponibilizar dentro do template Jinja2.</p>
</li>
<li><p><strong>"request": request -</strong> É <strong>fundamental</strong> incluir a chave "request" no contexto, mapeando para o objeto request original. O TemplateResponse e muitas funcionalidades do Jinja2 (como a função url_for, não usada aqui, mas comum) dependem disso para funcionar corretamente.</p>
</li>
<li><p><strong>templates.TemplateResponse("index.html", context)</strong>: Esta é a função que faz a mágica:</p>
<ul>
<li><p>Encontra o arquivo index.html no diretório templates.</p>
</li>
<li><p>Usa o motor Jinja2 para processar o template.</p>
</li>
<li><p>Substitui as variáveis e executa a lógica ({{ ... }}, {% ... %}) usando os dados do dicionário context.</p>
</li>
<li><p>Retorna uma resposta HTTP com o HTML renderizado e o Content-Type definido como text/html.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-executando-e-testando"><strong>Executando e testando</strong></h2>
<p>Certifique-se de que o uvicorn esteja rodando com o reload ativado:</p>
<pre><code class="lang-bash">uvicorn main:app --reload
</code></pre>
<ol>
<li><strong>Acesso sem query parameters:</strong><br /> Abra seu navegador e acesse: <code>http://127.0.0.1:8000/</code><br /> Você deverá ver a página HTML renderizada, mostrando os detalhes da sua requisição (User Agent, IP, etc.). Na seção "Query Parameters", você verá a mensagem <strong>"Nenhum query parameter encontrado."</strong>, pois a condição <code>{% if query_params %}</code> no template será falsa.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743525965405/d277065e-c873-4909-85c1-1ac527f81031.png" alt class="image--center mx-auto" /></p>
<ol>
<li><strong>Acesso com query parameters:</strong><br /> Agora, tente acessar a mesma URL, mas adicione alguns parâmetros na query string:<br /> <code>http://127.0.0.1:8000/?message=hello,%20world!</code><br /> Desta vez, a página será renderizada dinamicamente! Na seção <strong>"Query Parameters"</strong>, você verá as chaves e valores que passou (message, user, debug) listados na tabela, pois agora <em>query_params</em> no contexto contém esses dados e o loop <code>{% for %}</code> no template foi executado.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743528442505/9107594e-be1e-4f09-98af-3af2c4f87046.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusao"><strong>Conclusão</strong></h2>
<p>Como vimos, integrar <strong>FastAPI</strong> com <strong>Jinja2</strong> para renderizar HTML dinâmico é um processo direto e poderoso. Com apenas algumas linhas de configuração e a criação de um template HTML, você pode gerar páginas web diretamente do seu backend Python, passando dados da sua lógica de aplicação para a interface do usuário.</p>
<p>Esta abordagem é ideal para:</p>
<ul>
<li><p>Dashboards internos e painéis administrativos.</p>
</li>
<li><p>Páginas de conteúdo que precisam de dados dinâmicos.</p>
</li>
<li><p>Prototipagem rápida de interfaces.</p>
</li>
<li><p>Situações onde a complexidade de um framework SPA não se justifica.</p>
</li>
</ul>
<p>Lembre-se que o Jinja2 oferece muitos outros recursos, como herança de templates (para criar layouts base), macros (funções reutilizáveis), filtros mais avançados e muito mais, permitindo construir interfaces server-side bastante sofisticadas.</p>
<hr />
<p><strong>Gostou deste tutorial? Tem dúvidas ou sugestões? Deixe seu comentário abaixo!</strong></p>
]]></content:encoded></item><item><title><![CDATA[Aprimore a segurança do seu site WordPress com estas 10 dicas essenciais]]></title><description><![CDATA[Introdução
O WordPress é a ferramenta mais popular para criação de sites. Porém, essa popularidade também o torna alvo de ataques e injeção de códigos maliciosos. Relatórios da Sucuri comprovam essa vulnerabilidade, e minha experiência com inúmeros s...]]></description><link>https://blog.ricardotenv.dev/aprimore-a-seguranca-do-seu-site-wordpress-com-estas-10-dicas-essenciais</link><guid isPermaLink="true">https://blog.ricardotenv.dev/aprimore-a-seguranca-do-seu-site-wordpress-com-estas-10-dicas-essenciais</guid><category><![CDATA[WordPress]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Thu, 19 Dec 2024 12:55:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Zk--Ydz2IAs/upload/e9d2e6d1e73783d77e94e0fdc29b560f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introducao">Introdução</h3>
<p>O <strong>WordPress</strong> é a ferramenta mais popular para criação de sites. Porém, essa popularidade também o torna alvo de ataques e injeção de <strong>códigos maliciosos</strong>. Relatórios da <strong>Sucuri</strong> comprovam essa vulnerabilidade, e minha experiência com inúmeros serviços de remoção de malware reforça essa realidade.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726610078633/99bd6bb5-a697-453a-bada-09049e20a577.png" alt class="image--center mx-auto" /></p>
<p>Mas isso não significa que o WordPress seja ruim. A questão é que sua flexibilidade exige cuidados que muitos negligenciam. Muitos usuários contratam uma VPS barata, instalam o WordPress com um auto-installer e deixam o site sem manutenção.</p>
<p>Vamos mudar isso. Quero compartilhar algumas medidas eficazes que tenho aplicado com sucesso para meus clientes. Mesmo que você não seja técnico, você deve ler esse post pois <strong>algumas medidas não necessitam de aplicações técnicas</strong> e você pode enviar este post para quem cuida do seu site.</p>
<h3 id="heading-1-use-a-cloudflare">1 - Use a Cloudflare</h3>
<p>Quando o assunto é segurança, a <strong>Cloudflare</strong> é uma das melhores opções. Ela combina diversas <strong>ferramentas de infraestrutura e segurança</strong>, como o Fail2Ban por exemplo, e será extremamente útil para proteger seu site.</p>
<h3 id="heading-2-invista-em-manutencao">2 - Invista em manutenção</h3>
<p>Se for usar uma VPS, contrate alguém qualificado para fazer as configurações iniciais ou use a ferramenta que criei: <a target="_blank" href="https://github.com/ricardotenv/SetupNewVPSWithAnsible">SetupNewVPSWithAnsible</a>. Também é importante contratar periodicamente um profissional para a manutenção da máquina, garantindo as atualizações de segurança do sistema operacional. Esse serviço é acessível e evitará custos maiores com remoção de malware no futuro.</p>
<h3 id="heading-3-evite-paineis-de-controle-como-cpanel-ou-aapanel">3 - Evite painéis de controle como Cpanel ou Aapanel</h3>
<p>Entendo que você está acostumado com esse tipo de ferramenta e talvez não saiba como o <strong>Linux</strong> funciona sem ela. O problema é que esses painéis são apenas interfaces gráficas para o sistema, ao serem instalados no servidor, adicionam muitas coisas desnecessárias. Se não forem atualizados, podem causar problemas de segurança. Se for indispensável usá-los, prefira uma opção remota.</p>
<p>Esse tipo de ferramenta pode incentivar tanto a negligência com a devida manutenção do sistema quanto a realização de alterações desnecessárias ou arriscadas. Embora ofereça uma interface clicável, ela não substitui o conhecimento necessário para lidar com o <strong>Linux</strong> de forma adequada. Se você não tem experiência com o sistema, o ideal é não mexer em configurações que você não entende, mesmo com a aparente facilidade do painel.</p>
<h3 id="heading-4-use-criptografia-na-transferencia-de-dados-sftp-e-nao-ftp">4 - Use criptografia na transferência de dados (SFTP e não FTP)</h3>
<p>O File Transfer Protocol (FTP) é muito popular, especialmente com o uso do FileZilla, mas é inseguro. Ele transfere arquivos em texto simples, o que permite que um atacante intercepte a transferência. Por exemplo, no <strong>WordPress</strong>, a senha do banco de dados é armazenada no arquivo <strong>wp-config.php</strong> e isso é um puta risco nesse caso. Com <strong>SFTP (Secure File Transfer Protocol)</strong>, a transferência é encriptada, protegendo as informações. Além disso, a maioria das distribuições Linux não vem com <strong>FTP</strong> instalado, enquanto o <strong>SFTP</strong> usa o protocolo <strong>SSH</strong>, e quase todas as distros vêm com o <strong>OpenSSH</strong> instalado por padrão. Portanto, SFTP só tem vantagens.</p>
<h3 id="heading-5-faca-o-bloqueio-de-execucao-de-scripts-dentro-da-pasta-de-uploads">5 - Faça o bloqueio de execução de scripts dentro da pasta de uploads</h3>
<p>Essa dica é muito importante pois na maioria dos casos de infecção por malware em sites Wordpress, os scripts maliciosos são camuflados entre os arquivos de imagens que ficam dentro da pasta wp-content/uploads eu falo mais sobre isso e te ensino como fazer esse bloqueio no post:<br /><a target="_blank" href="https://blog.ricardotenv.dev/as-imagens-do-seu-site-wordpress-pode-ter-um-malware-aprenda-como-remover-e-prevenir"><strong>As imagens do seu site WordPress pode ter um malware! Aprenda como remover e prevenir</strong></a><strong>.</strong></p>
<h3 id="heading-6-desative-o-xml-rpc">6 - Desative o XML-RPC</h3>
<p>Esse arquivinho do core do WordPress permite a interação remota com o site e faz a ponte para aplicativos de terceiros. Hoje em dia, a <strong>API REST do WordPress</strong> já cobre muitas funções do <strong>XML-RPC</strong>, então, se você tem certeza de que não usa, desativar ele pode dar uma <strong>camada extra de segurança</strong>.</p>
<p><strong>Existem 3 formas bem simples de desativar esse arquivo:</strong></p>
<p>1. Desativar no <code>functions.php</code></p>
<p>Adicione este código no arquivo <code>functions.php</code> do seu tema:</p>
<pre><code class="lang-php">add_filter(<span class="hljs-string">'xmlrpc_enabled'</span>, <span class="hljs-string">'__return_false'</span>)
</code></pre>
<p>2. Bloquear o acesso via <code>.htaccess</code> Se estiver usando um servidor Apache, você pode bloquear diretamente o acesso adicionando esta regra ao arquivo <code>.htaccess</code></p>
<pre><code class="lang-php">&lt;Files xmlrpc.php&gt;
    Order Deny,Allow
    Deny <span class="hljs-keyword">from</span> all
&lt;/Files&gt;
</code></pre>
<p>3. E também é possível bloquear pelo Cloudflare e na minha opinião é a forma mais simples. Vá até <strong>Security</strong> → <strong>WAF</strong> → <strong>Custom rules</strong> e adiciona a regra como na imagem abaixo clicando em <strong>Deploy</strong> para finalizar:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730249363281/e6917414-9811-4631-b868-8f41c58cc15a.png" alt="bloquear xml-rpc na cloudflare" class="image--center mx-auto" /></p>
<h3 id="heading-7-desative-o-wp-cron">7 - Desative o WP-Cron</h3>
<p>O <strong>wp-cron</strong> é o carinha que cuida das <strong>tarefas agendadas no WordPress</strong>, como postagens programadas e atualizações automáticas. Ele é acionado pelas visitas, já que não usa o sistema de agendamento do SO, o que pode sobrecarregar o site com muito tráfego ou atrasar os agendamentos em casos de pouco acesso.</p>
<p>Se o seu WordPress está em uma VPS com SSH, recomendo trocar o wp-cron pelo cronjob real do sistema. É simples de fazer, olha só:</p>
<p>1. Desative o wp-cron adicionando a linha abaixo no arquivo <code>wp-config.php</code>:</p>
<pre><code class="lang-php">define(<span class="hljs-string">'DISABLE_WP_CRON'</span>, <span class="hljs-literal">true</span>);
</code></pre>
<p>2. Crie um cron job no sistema para acessar o wp-cron em intervalos regulares (exemplo, a cada 5 minutos):</p>
<pre><code class="lang-bash">*/5 * * * * curl -s https://seusite.com/wp-cron.php?doing_wp_cron &gt; /dev/null 2&gt;&amp;1
</code></pre>
<h3 id="heading-8-plugins-sao-perigosos">8 - Plugins são perigosos!</h3>
<p>Eu sempre passo uma <em>regrinha básica</em> pros meus clientes: <em>“se você não sabe ao certo o que o plugin faz, desinstale”</em>.</p>
<p><strong>Os plugins são os maiores vilões quando se fala em invasão de sites WordPress.</strong> Ter muitos plugins aumenta as chances de problemas nas atualizações, o que leva muita gente a não atualizar. E quando esses plugins ficam sem atualização, as falhas de segurança descobertas não são corrigidas, deixando o site vulnerável.</p>
<p>Outro ponto importante é que muitos plugins registram dados no banco, e se o desenvolvedor não cuidou bem disso, seu site pode sofrer com problemas de performance.</p>
<p>Por isso, o ideal é manter o mínimo de plugins possível.</p>
<h3 id="heading-9-desative-o-endpoint-user">9 - Desative o endpoint /user</h3>
<p>Como mencionado anteriormente, o WordPress conta com a REST API, que permite diversas integrações e recursos. No entanto, muita gente acaba não aproveitando essa ferramenta, o que pode deixá-la esquecida.</p>
<p>Um dos métodos mais comuns de ataque em sites WordPress é o de <strong>força bruta</strong>, que consiste em inúmeras tentativas de adivinhar combinações de usuário e senha para invadir o painel de controle.</p>
<p>Para reforçar a segurança do seu site, é uma boa ideia desativar o endpoint <code>/wp-json/wp/v2/users</code>. Esse recurso pode expor informações como o ID dos usuários, facilitando ataques. Caso você não precise dele, desativá-lo é uma forma simples de reduzir vulnerabilidades.</p>
<p>Você pode bloquear esse endpoint aplicando as configurações mencionadas anteriormente, como ajustar o arquivo <code>.htaccess</code> ou usar ferramentas como a Cloudflare.</p>
<h3 id="heading-10-faca-backups">10 - Faça backups</h3>
<p><strong>Esse ponto não pode ser ignorado</strong>. É impressionante como a negligência em relação aos backups ainda é tão comum entre donos de sites WordPress. Embora isso muitas vezes acabe gerando trabalho para desenvolvedores como eu, é triste ver pessoas perderem anos de esforço simplesmente por não terem tomado precauções básicas.</p>
<p>Não transfira essa responsabilidade para terceiros. Assim que o seu site estiver no ar, implemente uma solução de backup confiável. Isso pode ser feito contratando um desenvolvedor para automatizar o processo ou investindo em um bom plugin especializado. Mas lembre-se: <strong>nunca dependa apenas do backup da sua hospedagem</strong>.</p>
<p>Uma estratégia eficiente de backup precisa ser <strong>confiável, automática e redundante</strong>. Isso significa garantir que seus dados sejam salvos regularmente, em mais de um local, para que estejam acessíveis em caso de qualquer imprevisto.</p>
<p><em>Evite aprender essa lição da pior forma.</em></p>
<h3 id="heading-conclusao">Conclusão</h3>
<p>Reconheço que algumas partes deste post podem ter soado um pouco duras demais, mas minha intenção é ajudar. Trabalhando há anos com WordPress, já vi de tudo e, na maioria das vezes, os problemas surgem por negligência ou falta de atenção dos próprios usuários.</p>
<p>Existem muitas outras dicas que poderiam ser incluídas aqui, mas acredito que estas são as mais importantes para quem está começando a trabalhar com o WordPress. São práticas simples, mas que podem evitar grandes dores de cabeça no futuro.</p>
<p>O que achou das sugestões? Caso tenha dúvidas ou queira que eu explore algum dos tópicos mais a fundo, sinta-se à vontade para me enviar uma mensagem no <a target="_blank" href="https://www.linkedin.com/in/ricardotenv/">LinkedIn</a> ou me segue no <a target="_blank" href="https://www.threads.net/@ricardotenv">Threads</a>, eu estou bastante ativo por lá.</p>
]]></content:encoded></item><item><title><![CDATA[As imagens do seu site WordPress pode ter um malware! Aprenda como remover e prevenir]]></title><description><![CDATA[Introdução
Eu poderia listar vários pontos negativos do WordPress, mas não dá para criticar uma ferramenta que ajuda tantas pessoas a realizar seus sonhos. Então, vamos deixar isso para outro dia.
O maior problema do WordPress são seus usuários, muit...]]></description><link>https://blog.ricardotenv.dev/as-imagens-do-seu-site-wordpress-pode-ter-um-malware-aprenda-como-remover-e-prevenir</link><guid isPermaLink="true">https://blog.ricardotenv.dev/as-imagens-do-seu-site-wordpress-pode-ter-um-malware-aprenda-como-remover-e-prevenir</guid><category><![CDATA[WordPress]]></category><category><![CDATA[Security]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[wordpress themes]]></category><category><![CDATA[#wordpress]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Thu, 10 Oct 2024 14:51:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726687041477/2c4817f9-abe9-415f-86c1-47ca7dd384c1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introducao">Introdução</h2>
<p>Eu poderia listar vários pontos negativos do WordPress, mas não dá para criticar <strong>uma ferramenta que ajuda tantas pessoas</strong> a realizar seus sonhos. Então, vamos deixar isso para outro dia.</p>
<p><strong>O maior problema do WordPress são seus usuários</strong>, muitas vezes agências ou pessoas inexperientes de outras áreas. Isso é um sinal de sucesso da ferramenta, mas também resulta em muitos <strong>incidentes de infecção por malwares e perda de dados</strong>.</p>
<p><strong>Os malwares costumam ser obfuscados e camuflados entre os arquivos legítimos do WordPress</strong>. Neste post, vou mostrar como identificar esses arquivos maliciosos e evitar que eles sejam injetados no seu site.</p>
<ol>
<li><h2 id="heading-faca-uma-busca-por-scripts-maliciosos-entre-as-midias-do-seu-site">Faça uma busca por scripts maliciosos entre as mídias do seu site</h2>
<p> Abra o terminal do seu servidor e execute o seguinte comando:</p>
</li>
</ol>
<pre><code class="lang-bash">find wp-content/uploads/ -<span class="hljs-built_in">type</span> f \( -name <span class="hljs-string">"*.php"</span> \)
</code></pre>
<p>Este comando utiliza a ferramenta <code>find</code> do Linux para buscar arquivos ou pastas. Se você estiver utilizando o cPanel ou alguma ferramenta similar, pode ser ainda mais fácil, bastando utilizar a barra de pesquisa de diretórios.</p>
<p>Se for encontrado algum arquivo com a extensão <code>.php</code> pode ter certeza de que é um <strong>script malicioso</strong>, então basta usar o comando <code>rm</code> para remover. No entanto, <strong>certifique-se de que ele está entre as imagens</strong>.</p>
<ol start="2">
<li><h2 id="heading-faca-o-bloqueio-preventivo-a-scripts-maliciosos">Faça o bloqueio preventivo a scripts maliciosos</h2>
</li>
</ol>
<p>Uma das técnicas mais utilizadas nessas infecções é a proliferação do vírus através de requisições nesses scripts. Ou seja, o atacante injeta um script que cria mais dois ao ser executado. Nesse caso, a melhor prevenção é <strong>bloquear esse tipo de requisição nas pastas onde você sabe que esses arquivos não deveriam estar</strong>. A seguir, vou mostrar como fazer isso de duas formas.</p>
<h3 id="heading-bloqueando-scripts-maliciosos-entre-as-imagens-do-wordpress-atraves-do-proxy-reverso-nginx">Bloqueando scripts maliciosos entre as imagens do Wordpress através do Proxy Reverso (Nginx)</h3>
<p>Se você ainda não sabe como funciona um proxy reverso, leia o meu post <a target="_blank" href="https://blog.ricardotenv.dev/proxy-x-proxy-reverso-entenda-de-uma-vez-por-todas-a-diferenca">Proxy x Proxy Reverso. Entenda de uma vez por todas a diferença</a>.</p>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">server_name</span> exemplo.com;

    <span class="hljs-attribute">location</span> /wp-content/uploads {
        <span class="hljs-comment"># Bloqueia arquivos .php</span>
        <span class="hljs-attribute">if</span> (<span class="hljs-variable">$request_uri</span> <span class="hljs-regexp">~* \.(php)$)</span> {
            <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;  <span class="hljs-comment"># Retorna erro 403 Forbidden</span>
        }

        <span class="hljs-attribute">proxy_pass</span> http://backend_servidor;
        <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$host</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;
    }
}
</code></pre>
<p>Não esqueça de reiniciar o Nginx para aplicar a nova regra:</p>
<pre><code class="lang-bash">sudo systemctl restart nginx
</code></pre>
<h3 id="heading-bloqueando-scripts-maliciosos-entre-as-imagens-do-wordpress-atraves-do-cloudflare">Bloqueando scripts maliciosos entre as imagens do Wordpress através do Cloudflare.</h3>
<p>O <strong>Cloudflare</strong> é uma plataforma que melhora a segurança e o desempenho de sites e aplicações online, protegendo contra <strong>ataques DDoS</strong> e otimizando a velocidade com caching e uma rede de distribuição de conteúdo (CDN). Ele também oferece <strong>firewall</strong> e <strong>proteção contra bots</strong>, garantindo mais segurança e eficiência para sites e APIs.</p>
<p>Vamos utilizar de uma dessas ferramentas para <strong>prevenir a injeção de scripts maliciosos dentro das imagens do seu site</strong>.</p>
<p>Ao entrar no painel do seu domínio, no menu lateral esquerdo, vá em <strong>Security</strong> → <strong>WAF</strong> → <strong>Custom rules</strong> e adicione a seguinte regra:</p>
<pre><code class="lang-bash">(http.request.uri.path contains <span class="hljs-string">"/wp-content/uploads/"</span> and ends_with(http.request.uri.path, <span class="hljs-string">".php"</span>))
</code></pre>
<p>Após isso, sempre que o atacante tentar acessar o endpoint que executa o script injetado, ele verá uma tela como esta:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728562475484/6a0fab2a-5b8e-4719-afb4-daa6a7b863fe.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusao">Conclusão</h2>
<p><strong>Proteger seu site WordPress contra malwares</strong> é uma tarefa contínua, especialmente com a facilidade com que esses scripts maliciosos podem se esconder entre as mídias. Ao seguir os passos deste post, você estará garantindo que seu site fique mais seguro, <strong>bloqueando vulnerabilidades comuns</strong>. Não se esqueça: a segurança é uma maratona, não uma corrida de 100 metros. Continuar monitorando e adotando boas práticas vai fazer toda a diferença para evitar futuras infecções. Afinal, ninguém quer um site comprometido, certo? 💻🔒</p>
]]></content:encoded></item><item><title><![CDATA[Proxy x Proxy Reverso. Entenda de uma vez por todas a diferença]]></title><description><![CDATA[Introdução
Para profissionais de tecnologia, o uso de proxies e proxies reversos é essencial em ambientes de rede e infraestrutura de servidores. Ambos os tipos desempenham papéis específicos na mediação de conexões, mas com finalidades distintas e l...]]></description><link>https://blog.ricardotenv.dev/proxy-x-proxy-reverso-entenda-de-uma-vez-por-todas-a-diferenca</link><guid isPermaLink="true">https://blog.ricardotenv.dev/proxy-x-proxy-reverso-entenda-de-uma-vez-por-todas-a-diferenca</guid><category><![CDATA[proxy]]></category><category><![CDATA[Proxy Server]]></category><category><![CDATA[cloudflare]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Sat, 28 Sep 2024 18:57:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726870230831/5393479a-5e61-4d7d-afa9-795bb2a301e5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introducao">Introdução</h2>
<p>Para profissionais de <strong>tecnologia</strong>, o uso de <strong>proxies</strong> e <strong>proxies reversos</strong> é essencial em ambientes de <strong>rede</strong> e <strong>infraestrutura de servidores</strong>. Ambos os tipos desempenham papéis específicos na mediação de conexões, mas com finalidades distintas e localizações diferentes na cadeia de comunicação. Eu vou abordar as características técnicas de cada um e como são utilizados em práticas de <strong>segurança</strong>, <strong>otimização de desempenho</strong> e <strong>controle de acesso</strong> em redes corporativas e ambientes de produção.</p>
<h2 id="heading-o-que-e-um-forward-proxy-ou-apenas-proxy">O que é um Forward Proxy (ou apenas Proxy)?</h2>
<p>Um <em>Forward Proxy</em> é um <strong>servidor intermediário</strong> que atua entre o <strong>cliente</strong> e os <strong>servidores da internet</strong>. Localizado <em>"do lado do cliente"</em>, ele intercepta e redireciona <strong>requisições de saída</strong>, aplicando políticas de controle e segurança antes que cheguem ao destino. Esse tipo de proxy é frequentemente implementado para:</p>
<ul>
<li><p><strong>Controle de Acesso</strong>: Permite bloquear ou restringir o acesso a determinados sites ou serviços com base em políticas da organização. Por exemplo, o acesso a redes sociais ou conteúdo inapropriado pode ser filtrado no proxy.</p>
</li>
<li><p><strong>Registro e Monitoramento</strong>: Todas as <strong>requisições</strong> são registradas, facilitando a <strong>auditoria</strong> e o rastreamento de atividades na rede. Esse log detalha o endereço IP de origem, o conteúdo acessado e os horários das requisições.</p>
</li>
<li><p><strong>Anônimato e Bypass de Restrições</strong>: Em alguns casos, o <em>Forward Proxy</em> pode ser utilizado para contornar <strong>restrições geográficas</strong>. Isso ocorre quando um usuário se conecta a um proxy localizado em uma região permitida, que realiza a requisição em nome do cliente, mascarando a origem da solicitação.</p>
</li>
</ul>
<h3 id="heading-usos-comuns-do-forward-proxy">Usos Comuns do Forward Proxy:</h3>
<ul>
<li><p><strong>Segurança</strong>: Bloqueio de <strong>conteúdo malicioso</strong> ou <strong>sites não autorizados</strong>.</p>
</li>
<li><p><strong>Filtragem de Conteúdo</strong>: Restrição de acesso com base em categorias de sites.</p>
</li>
<li><p><strong>Anonimização</strong>: Ocultação de IPs de origem, especialmente em redes públicas.</p>
</li>
<li><p><strong>Bypass de Geolocalização</strong>: Acesso a conteúdo restrito em determinadas regiões.</p>
</li>
</ul>
<h2 id="heading-o-que-e-um-reverse-proxy-proxy-reverso">O que é um Reverse Proxy (Proxy Reverso)?</h2>
<p>Um <strong>Reverse Proxy</strong> opera do lado do <strong>servidor</strong> e age como um intermediário entre os <strong>clientes</strong> e um ou mais <strong>servidores back-end</strong>. Diferentemente do Forward Proxy, o Reverse Proxy é transparente para o cliente e sua função principal é gerenciar e otimizar as requisições que chegam aos servidores de aplicação.</p>
<p>O Reverse Proxy recebe as requisições do cliente e, dependendo da configuração, distribui essas requisições para diferentes servidores. Ele também pode desempenhar funções adicionais, como:</p>
<ul>
<li><p><strong>Balanceamento de Carga</strong>: O Reverse Proxy distribui o tráfego entre vários servidores back-end, evitando sobrecarga em um único servidor e aumentando a disponibilidade e a resiliência da aplicação.</p>
</li>
<li><p><strong>Cache</strong>: Implementar cache de conteúdo no Reverse Proxy permite que ele responda a requisições com dados previamente armazenados, reduzindo a carga nos servidores back-end e melhorando o tempo de resposta.</p>
</li>
<li><p><strong>Proteção contra Ataques DDoS</strong>: Como o Reverse Proxy atua como uma camada entre o cliente e o servidor, ele pode mitigar ataques de negação de serviço distribuída (DDoS), filtrando requisições maliciosas antes que cheguem ao servidor principal.</p>
</li>
<li><p><strong>SSL Offloading</strong>: O Reverse Proxy pode gerenciar a criptografia SSL/TLS, descarregando o processamento criptográfico dos servidores de aplicação, o que libera recursos de CPU para outras tarefas.</p>
</li>
</ul>
<h3 id="heading-usos-comuns-do-reverse-proxy">Usos Comuns do Reverse Proxy:</h3>
<ul>
<li><p><strong>Balanceamento de Carga</strong>: Redistribuição de tráfego entre múltiplos servidores.</p>
</li>
<li><p><strong>Cache</strong>: Melhoria no desempenho e redução da carga nos servidores.</p>
</li>
<li><p><strong>Proteção Anti-DDoS</strong>: Filtragem de requisições maliciosas e limitação de taxa.</p>
</li>
<li><p><strong>Criptografia e Descriptografia</strong>: SSL offloading para reduzir a carga de trabalho dos servidores back-end.</p>
</li>
<li><p><strong>Segurança</strong>: Controle de acesso e proteção contra ameaças ao ocultar a infraestrutura de servidores.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726869838223/17ba462a-f755-4113-817f-3b27e7682102.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Se você é desenvolvedor, compreender profundamente os conceitos de <strong>Forward Proxy</strong> e <strong>Reverse Proxy</strong> é essencial, pois ambos desempenham papéis importantes em <strong>arquiteturas de rede</strong> e <strong>sistemas distribuídos</strong>. No seu dia a dia, você provavelmente usará essas soluções para otimizar o desempenho de aplicações, implementar medidas de segurança e resolver problemas complexos relacionados a controle de acesso e distribuição de carga. Dominar esses conceitos permitirá que você enfrente desafios técnicos com maior eficiência, facilitando a tomada de decisões estratégicas em diversas situações, como na proteção de servidores, balanceamento de tráfego e escalabilidade de serviços.</p>
]]></content:encoded></item><item><title><![CDATA[Como criar um site com Python puro, sem framework (usando CGI)]]></title><description><![CDATA[O que é CGI?
CGI (Common Gateway Interface) é uma especificação padrão que permite a um servidor web transferir dados para e de aplicações externas. Na prática, CGI permite a execução de scripts que geram conteúdo dinâmico em um servidor web.
Funcion...]]></description><link>https://blog.ricardotenv.dev/como-criar-um-site-com-python-puro-sem-framework-usando-cgi</link><guid isPermaLink="true">https://blog.ricardotenv.dev/como-criar-um-site-com-python-puro-sem-framework-usando-cgi</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[cgi]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Tue, 18 Jun 2024 20:22:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/SGW7enB7bl0/upload/7a7a5393b58c77c3f4d6cfe339c60534.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-o-que-e-cgi">O que é CGI?</h3>
<p><strong>CGI (Common Gateway Interface)</strong> é uma especificação padrão que permite a um servidor web transferir dados para e de aplicações externas. Na prática, CGI permite a execução de scripts que geram conteúdo dinâmico em um servidor web.</p>
<h3 id="heading-funcionamento">Funcionamento</h3>
<p>Quando uma requisição atende uma diretiva configurada no servidor, ele cria um processo adicional e passa as informações da requisição via <strong>stdin</strong> para o script configurado. Este script, que pode ser escrito em diversas linguagens como <strong>Python</strong>, processa os dados e gera o conteúdo dinâmico que é devolvido ao servidor e, por fim, ao cliente (navegador).</p>
<h3 id="heading-cenario-pratico">Cenário Prático</h3>
<p>Imagine que você precisa criar um formulário para coletar dados rapidamente e só tem um servidor virtual com Python disponível. Usando CGI, você pode configurar um servidor web rapidamente sem precisar de frameworks como Flask ou Django.</p>
<p>"Mas Ricardo, por que não usar algo como FastAPI?" Porque você tem pouco tempo para cumprir essa tarefa e não pode criar mais um serviço para o <strong>SRE</strong> da sua empresa monitorar.</p>
<p>Vou pular a parte de configurar a máquina e supor que você já leu o artigo <a target="_blank" href="https://blog.ricardotenv.dev/como-reforcar-rapidamente-a-seguranca-da-sua-nova-vps-com-um-playbook-ansible-simples">como configurar uma nova VPS Linux com segurança usando o Ansible</a> e já tem acesso seguro à máquina fornecida.</p>
<p>E se você ainda não tem acesso à uma VPS você pode <a target="_blank" href="https://m.do.co/c/844075225478">começar com $200 de créditos usando meu link</a> da <strong>DigitalOcean</strong>.</p>
<h3 id="heading-passos-para-implementacao">Passos para Implementação</h3>
<ol>
<li><strong>Instale o Apache</strong></li>
</ol>
<pre><code class="lang-bash">sudo apt-get install apache2
</code></pre>
<p>Use o comando <code>python3 --version</code> para verificar se a máquina já tem o Python instalado, na maioria das distribuições Linux ele já vem instalado por padrão.</p>
<ol start="2">
<li><strong>Ative o módulo CGI no Apache</strong>:</li>
</ol>
<pre><code class="lang-bash">sudo a2enmod cgi
</code></pre>
<ol start="3">
<li><strong>Configure o Apache</strong>:</li>
</ol>
<p>Adicione a seguinte diretiva no arquivo de configuração do <strong>Apache</strong> para permitir a execução de scripts CGI:</p>
<pre><code class="lang-apache"><span class="hljs-section">&lt;Directory <span class="hljs-string">"/var/www/html/"</span>&gt;</span>
    <span class="hljs-attribute">AllowOverride</span> None
    <span class="hljs-attribute"><span class="hljs-nomarkup">Options</span></span> +ExecCGI
    <span class="hljs-attribute">AddHandler</span> cgi-script .py
    <span class="hljs-attribute">Require</span> <span class="hljs-literal">all</span> granted
<span class="hljs-section">&lt;/Directory&gt;</span>
</code></pre>
<p>Neste exemplo, qualquer arquivo dentro do diretório /var/www/html/ com a extensão .py será tratado como um script CGI e executado como tal.</p>
<ol start="4">
<li><strong>Crie e configure um script Python</strong>:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /var/www/html
touch hello.py
chmod +x hello.py
</code></pre>
<p>Conteúdo do <code>hello.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

<span class="hljs-keyword">import</span> os

print(<span class="hljs-string">"Content-type: text/html\n"</span>)
print(<span class="hljs-string">"&lt;html&gt;&lt;head&gt;&lt;title&gt;CGI Info&lt;/title&gt;&lt;/head&gt;&lt;body&gt;"</span>)
print(<span class="hljs-string">"&lt;h1&gt;CGI Info&lt;/h1&gt;"</span>)

<span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> os.environ.items():
    print(<span class="hljs-string">f"&lt;p&gt;&lt;strong&gt;<span class="hljs-subst">{key}</span>:&lt;/strong&gt; <span class="hljs-subst">{value}</span>&lt;/p&gt;"</span>)

print(<span class="hljs-string">"&lt;/body&gt;&lt;/html&gt;"</span>)
</code></pre>
<p>A simplicidade desse script é notável: o servidor web utiliza o <em>shebang</em> para identificar o interpretador a ser usado e passa várias variáveis de ambiente definidas pelo Apache no processo CGI atual. Essas variáveis podem variar com novas solicitações no mesmo endereço, refletindo diferentes dados da requisição. Isso permite que o script CGI responda dinamicamente com base nas informações da requisição.</p>
<p>Agora basta acessar a porta 80 do servidor, por exemplo, <a target="_blank" href="http://146.190.78.111/hello.py">http://146.190.78.111/hello.py</a> e se você ver algo como a imagem abaixo, é porque já está funcionando.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717779955640/5d8a40ee-c53c-42fb-91f1-b408650b35b4.png" alt class="image--center mx-auto" /></p>
<p>Eu poderia encerrar aqui, pois você já aprendeu <strong>como o CGI funciona</strong> e como servir um site em <strong>Python sem um framework web</strong>. No entanto, vamos dar um passo adiante: criar um formulário, coletar os dados e ver tudo funcionando na prática.</p>
<h3 id="heading-criacao-de-um-formulario">Criação de um Formulário</h3>
<p>Crie um formulário HTML que coleta dados e os envia para um script CGI Python que processa esses dados e os salva em um arquivo CSV.</p>
<ol>
<li><strong>Formulário HTML</strong> <code>formulario.py</code>:</li>
</ol>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

print(<span class="hljs-string">"Content-type: text/html\n"</span>)
print(<span class="hljs-string">"""
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Formulário CGI&lt;/title&gt;
    &lt;link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;h1 class="mt-5"&gt;Formulário de Sugestões&lt;/h1&gt;
        &lt;form method="post" action="/process_form.py"&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="nome"&gt;Nome&lt;/label&gt;
                &lt;input type="text" class="form-control" id="nome" name="name" required&gt;
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="sobrenome"&gt;Sobrenome&lt;/label&gt;
                &lt;input type="text" class="form-control" id="sobrenome" name="surname" required&gt;
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="cargo"&gt;Cargo&lt;/label&gt;
                &lt;select class="form-control" id="cargo" name="office" required&gt;
                    &lt;option value="Desenvolvedor"&gt;Desenvolvedor&lt;/option&gt;
                    &lt;option value="Designer"&gt;Designer&lt;/option&gt;
                    &lt;option value="Gerente"&gt;Gerente&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="sugestao"&gt;Sugestão&lt;/label&gt;
                &lt;textarea class="form-control" id="sugestao" name="suggestion" rows="4" required&gt;&lt;/textarea&gt;
            &lt;/div&gt;
            &lt;button type="submit" class="btn btn-primary"&gt;Enviar&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
"""</span>)
</code></pre>
<p>Veja só, que interessante, o que esse script faz é apenas printar um código HTML e o navegador renderiza isso para o usuário como uma página, veja como ficou:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717844001181/9d20ff59-bc77-459b-a914-fec2f38f05f7.png" alt class="image--center mx-auto" /></p>
<p>Mas por que isso acontece? O CGI envia os dados do stdout para o servidor web, que os renderiza para o cliente. Quando usamos <code>print</code>, estamos enviando dados para o stdout. A primeira linha após o shebang define o tipo de conteúdo que o servidor deve renderizar, como HTML, mas poderia ser JSON, por exemplo.</p>
<p>E vamos para a parte mais interessante, quando esse formulário é preenchido e enviado, ele faz uma requisição POST para outro script, <code>proccess_form.py</code> que também será servido como CGI. Vamos criar o script que processará essa requisição, salvando os dados em um arquivo CSV no servidor. Esse script aceitará apenas requisições do tipo POST.</p>
<p>Para começar, importamos as bibliotecas necessárias. Em seguida, configuramos o tipo de conteúdo que será enviado para a saída padrão <em>stdout</em> e implementamos uma condição para permitir somente requisições do tipo POST. Caso uma requisição do tipo GET seja feita no script, uma mensagem de erro será retornada.</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

<span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys

print(<span class="hljs-string">"Content-type: text/html\n"</span>)

<span class="hljs-keyword">if</span> os.environ[<span class="hljs-string">'REQUEST_METHOD'</span>] == <span class="hljs-string">'POST'</span>:

<span class="hljs-keyword">else</span>:
    print(<span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Método não permitido. Use POST.&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span>)
</code></pre>
<p>Agora precisamos recuperar algumas variáveis de ambiente criadas pelo servidor, assim como fizemos com <code>REQUEST_METHOD</code>. Vamos usar esse método para acessar o conteúdo do corpo da requisição. Poderíamos usar uma <strong>biblioteca Python</strong> chamada <strong>cgi</strong>, mas neste caso, vamos fazer manualmente para facilitar o entendimento de como isso funciona.</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

<span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys

print(<span class="hljs-string">"Content-type: text/html\n"</span>)

<span class="hljs-keyword">if</span> os.environ[<span class="hljs-string">'REQUEST_METHOD'</span>] == <span class="hljs-string">'POST'</span>:
    content_length = int(os.environ[<span class="hljs-string">'CONTENT_LENGTH'</span>]
    post_data = sys.stdin.read(content_length)

<span class="hljs-keyword">else</span>:
    print(<span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Método não permitido. Use POST.&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span>)
</code></pre>
<p>O que estamos fazendo nesse trecho é pegar o comprimento dos dados do corpo da requisição. Como já sabemos, uma variável de ambiente é uma string, então a convertemos para inteiro. Com o comprimento em mãos, pegamos exatamente os dados que queremos no <em>stdin</em>. Assim, teremos armazenado em <code>post_data</code> algo como <code>name=Ricardo&amp;surname=Santos&amp;office=Developer&amp;suggestion=Mais+café.</code></p>
<p>E então, <em>parseamos</em> esses dados para usá-los mais adiante.</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

<span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys

print(<span class="hljs-string">"Content-type: text/html\n"</span>)

<span class="hljs-keyword">if</span> os.environ[<span class="hljs-string">'REQUEST_METHOD'</span>] == <span class="hljs-string">'POST'</span>:
    content_length = int(os.environ[<span class="hljs-string">'CONTENT_LENGTH'</span>]
    post_data = sys.stdin.read(content_length)

    params = post_data.split(<span class="hljs-string">'&amp;'</span>)
    form_data = {}

    <span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> params:
        key, value = param.split(<span class="hljs-string">'='</span>)
        form_data[key] = value

    name = form_data[<span class="hljs-string">'name'</span>]
    surname = form_data[<span class="hljs-string">'surname'</span>]
    office = form_data[<span class="hljs-string">'office'</span>]
    suggestion = form_data[<span class="hljs-string">'suggestion'</span>]
<span class="hljs-keyword">else</span>:
    print(<span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Método não permitido. Use POST.&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span>)
</code></pre>
<p>E por último vamos salvar os dados desse formulário em uma planilha no formato csv.</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/python3</span>

<span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys

print(<span class="hljs-string">"Content-type: text/html\n"</span>)

<span class="hljs-keyword">if</span> os.environ[<span class="hljs-string">'REQUEST_METHOD'</span>] == <span class="hljs-string">'POST'</span>:
    content_length = int(os.environ[<span class="hljs-string">'CONTENT_LENGTH'</span>]
    post_data = sys.stdin.read(content_length)

    params = post_data.decode(<span class="hljs-string">'utf-8'</span>).split(<span class="hljs-string">'&amp;'</span>)
    form_data = {}

    <span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> params:
        key, value = param.split(<span class="hljs-string">'='</span>)
        form_data[key] = value

    name = form_data[<span class="hljs-string">'name'</span>]
    surname = form_data[<span class="hljs-string">'surname'</span>]
    office = form_data[<span class="hljs-string">'office'</span>]
    suggestion = form_data[<span class="hljs-string">'suggestion'</span>]

     <span class="hljs-keyword">with</span> open(<span class="hljs-string">'/var/www/html/sugestoes.csv'</span>, <span class="hljs-string">'a'</span>, newline=<span class="hljs-string">''</span>) <span class="hljs-keyword">as</span> csvfile:
        fieldnames = [<span class="hljs-string">'Nome'</span>, <span class="hljs-string">'Sobrenome'</span>, <span class="hljs-string">'Cargo'</span>, <span class="hljs-string">'Sugestao'</span>]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

        <span class="hljs-keyword">if</span> csvfile.tell() == <span class="hljs-number">0</span>:
            writer.writeheader()

        writer.writerow({<span class="hljs-string">'Nome'</span>: name, <span class="hljs-string">'Sobrenome'</span>: surname, <span class="hljs-string">'Cargo'</span>: office, <span class="hljs-string">'Sugestao'</span>: suggestion})
        print(<span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Obrigado pela sua sugestão!&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span>)
<span class="hljs-keyword">else</span>:
    print(<span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Método não permitido. Use POST.&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span>)
</code></pre>
<p>Antes de testar, não se esqueça de converter o script em um executável. Se você tiver algum problema de permissões, execute o comando abaixo para conceder essas permissões ao Apache.</p>
<pre><code class="lang-bash">sudo chmod +x /var/www/html/process_form.py
sudo chown -R www-data:www-data /var/www/html/
sudo chmod -R 775 /var/www/html/
</code></pre>
<h2 id="heading-conclusao">Conclusão</h2>
<p>O <strong>CGI</strong> foi amplamente utilizado no passado, mas hoje em dia não é recomendado para tarefas complexas. Isso ocorre porque o padrão <strong>CGI</strong> consome muitos recursos, já que um novo processo é aberto para cada requisição. Atualmente, existem métodos mais eficientes para gerar conteúdo dinâmico. Pretendo escrever novas versões deste post para abordar essas formas mais modernas e eficientes.</p>
<p>Embora este método seja bastante simples, ele é essencial para quem deseja aprender e trabalhar com <strong>arquiteturas de micro-serviços</strong>. Compreender os fundamentos do CGI pode fornecer uma base sólida para entender como as requisições e respostas funcionam na web. Além disso, ao dominar esses conceitos básicos, você estará melhor preparado para avançar para tecnologias mais avançadas e eficientes, como <strong>FastCGI</strong>, <strong>WSGI</strong>, ou até mesmo frameworks modernos como Flask e Django, que são amplamente utilizados para desenvolver aplicações web robustas e escaláveis.</p>
<p>Portanto, mesmo que o CGI não seja a melhor escolha para projetos complexos, aprender sobre ele pode ser um passo importante na sua jornada como desenvolvedor.</p>
]]></content:encoded></item><item><title><![CDATA[Será que as empresas e startups estão realmente se afastando das clouds públicas como AWS, Google Cloud e Azure?]]></title><description><![CDATA[Essa tem sido uma discussão recorrente em fóruns internacionais de tecnologia, especialmente no Reddit. Além disso, tenho dialogado com diversas pessoas sobre essa tendência, o que me motivou a trazer o debate para o contexto brasileiro, buscando ent...]]></description><link>https://blog.ricardotenv.dev/sera-que-as-empresas-e-startups-estao-realmente-se-afastando-das-clouds-publicas-como-aws-google-cloud-e-azure</link><guid isPermaLink="true">https://blog.ricardotenv.dev/sera-que-as-empresas-e-startups-estao-realmente-se-afastando-das-clouds-publicas-como-aws-google-cloud-e-azure</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[finops]]></category><category><![CDATA[AWS FinOps]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[Azure]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Fri, 10 May 2024 11:00:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714499475104/d44ac56a-becb-45cf-a0d2-1dcf70721da2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Essa tem sido uma discussão recorrente em fóruns internacionais de tecnologia, especialmente no <strong>Reddit</strong>. Além disso, tenho dialogado com diversas pessoas sobre essa tendência, o que me motivou a trazer o debate para o contexto brasileiro, buscando entender a perspectiva br sobre o tema qual é sempre a melhor de entender.</p>
<p>No último ano, deparei-me com um artigo de <strong>David Hansson</strong> criador do <strong>Ruby On Rails</strong> e um grande empresário, cujo link compartilharei ao final deste texto. Nele, <strong>Hansson</strong> revela sua decisão de migrar seu principal produto para uma <strong>infraestrutura própria</strong>, o que me inspirou a refletir sobre os diversos aspectos que serão abordados ao longo deste artigo.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://world.hey.com/dhh/we-have-left-the-cloud-251760fb">https://world.hey.com/dhh/we-have-left-the-cloud-251760fb</a></div>
<p> </p>
<h2 id="heading-vale-a-pena-hospedar-uma-aplicacao-em-uma-cloud-publica">Vale a pena hospedar uma aplicação em uma Cloud pública?</h2>
<p>Para iniciar essa discussão, recomendo assistir a um vídeo do <strong>Fireship</strong>, no qual é sugerido que as clouds públicas podem ser comparadas a uma droga: viciantes e difíceis de abandonar.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/4Wa5DivljOM?si=-v_zr5FzZUgRJv4W">https://youtu.be/4Wa5DivljOM?si=-v_zr5FzZUgRJv4W</a></div>
<p> </p>
<p>Adicionalmente, um comentário interessante surgiu em um vídeo de <strong>ThePrimeagen</strong>, que reagia ao vídeo mencionado acima:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714137726580/0d073e18-119c-4bf5-830f-4f362899c596.png" alt class="image--center mx-auto" /></p>
<p>Nos últimos tempos, conversei com vários clientes que iniciaram seus empreendimentos na nuvem. Alguns expressaram arrependimento, especialmente após o término dos <strong>créditos de teste</strong>, momento em que se depararam com as primeiras faturas elevadas. E, como muitos já perceberam, seja através do vídeo ou da própria experiência, a estratégia de negócios desses provedores de cloud parece tornar a migração para infraestruturas mais alinhadas ao faturamento da empresa um desafio bastante "doloroso".</p>
<p>No artigo mencionado, <strong>Hansson</strong> discute o que ele considera uma das maiores ilusões do mercado de software: a crença de que a hospedagem em cloud seria menos complexa e mais econômica do que manter uma infraestrutura própria. Contrariando essa percepção, <strong>Hansson</strong> argumenta que gerenciar serviços em cloud pode ser tão complexo quanto manter uma infraestrutura própria. E eu destaco, por exemplo, a complexidade da interface web da <strong>AWS</strong> e a necessidade de aprender e dominar ferramentas não tão intuitivas, como o <strong>Terraform</strong>, para automatizar tarefas.</p>
<p>Em um artigo subsequente (link abaixo), <strong>Hansson</strong> compartilha sua experiência pós-migração, revelando uma economia significativa de aproximadamente 1 milhão de dólares por ano. Esse dado reforça a ideia de que, embora a migração para uma infraestrutura própria possa apresentar desafios iniciais, os benefícios financeiros a longo prazo podem ser consideráveis.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://world.hey.com/dhh/our-cloud-exit-has-already-yielded-1m-year-in-savings-db358dea">https://world.hey.com/dhh/our-cloud-exit-has-already-yielded-1m-year-in-savings-db358dea</a></div>
<p> </p>
<p>Diante dos diversos indícios, torna-se evidente que a adoção de uma <strong>cloud pública</strong> não deve ser uma escolha automática ou padrão, como tem sido prática comum atualmente. Essa decisão necessita de uma avaliação criteriosa, considerando a complexidade e o alto custo envolvidos.</p>
<p>Existem situações específicas em que a hospedagem em cloud pública se justifica plenamente. Por exemplo, quando há exigências rigorosas de <strong>compliance</strong> que precisam ser atendidas ou quando o modelo de negócio está alinhado com investidores de perfil mais conservador, que veem na cloud uma forma de garantir maior segurança e estabilidade para a aplicação. Nestes casos, a escolha pela cloud pode ser não apenas apropriada, mas também necessária.</p>
<h2 id="heading-dominar-uma-cloud-deveria-ser-um-criterio-essencial-para-contratacao">Dominar uma cloud deveria ser um critério essencial para contratação?</h2>
<p>Após a leitura desse artigo citado anteriormente, e o consequente desligamento da minha posição como <strong>DevOps/SRE</strong>, deparei-me com a necessidade de buscar uma nova oportunidade no mercado. Foi então que percebi uma tendência nas vagas para essa especialidade: a exigência de anos de experiência com a AWS, critério com o qual eu não me enquadrava devido à minha experiência em um ambiente onde desenvolvíamos nossas próprias soluções. Essa experiência, embora enriquecedora e fundamental para o meu entendimento prático do conceito de cloud sem a intermediação de camadas de abstração, foi um dos fatores que me levaram a reconsiderar minha carreira na especialidade. Afinal, sempre tive mais interesse em criar soluções do que em manter sistemas operando.</p>
<p>Atualmente, tenho grande apreço pela AWS, <em>misturo no arroz e feijão e como no almoço e na janta</em> 😂. E reconheço que, ao compreender os mecanismos internos de funcionamento das clouds, tudo se torna mais acessível.</p>
<p>Contudo, questiono a relevância de tornar essa habilidade um requisito tão decisivo na contratação. Ao fazer isso, uma empresa pode acabar limitando sua operação à dependência de um único fornecedor. Se estivesse em posição de contratar especialistas hoje, valorizaria o conhecimento em conceitos fundamentais como <strong>Serverless Functions</strong>, <strong>CI/CD</strong>, <strong>VPC</strong>, <strong>VPN</strong>, clusterização, além da habilidade em <strong>orquestrar containers</strong> e <strong>máquinas virtuais</strong> na unha. Essas competências permitem a um profissional adaptar-se com facilidade a ambientes <strong>híbridos</strong> ou <strong>multi-cloud</strong>. Inclusive, se um candidato possuísse experiência em outra cloud, como o <strong>GCP</strong>, eu consideraria sua candidatura para posições relacionadas à <strong>AWS</strong>, dado que as ferramentas, embora variem em quantidade, são essencialmente as mesmas em diferentes plataformas. Naturalmente, para negócios já profundamente integrados a uma cloud específica, faz sentido buscar profissionais com experiência direta nessa plataforma. No entanto, é curioso observar empresas <strong>on-premise</strong> que estabelecem a AWS como um requisito, o que pode refletir uma compreensão limitada das necessidades reais do negócio.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>O futuro das <strong>infraestruturas de TI</strong> permanece uma incógnita. Não está claro se as clouds públicas dominarão completamente o cenário ou se as empresas buscarão alternativas, nem se haverá intervenção governamental frente ao crescente monopólio dessas plataformas. O que é certo, porém, é o aumento contínuo no faturamento dessas empresas, um indicador que pode muito bem sinalizar as tendências futuras.</p>
<p>Para você, <strong>microempreendedor</strong>, deixo uma sugestão valiosa: desenvolva suas aplicações utilizando containers desde o início, mesmo que seu projeto seja um <strong>monolito</strong> na fase inicial de validação do negócio. Essa abordagem oferece flexibilidade significativa, permitindo que sua aplicação seja hospedada em diversos ambientes sem a necessidade de alterações estruturais. Seja através do <strong>ECS</strong> da Amazon com <strong>EC2</strong> ou <strong>Fargate</strong>, <strong>Kubernetes</strong> em <strong>serverless</strong> na <strong>Digital Ocean</strong> ou <strong>GCP</strong>, em sua própria <strong>VPS</strong>, ou até mesmo em um computador em sua casa, a utilização de containers assegura que você possa migrar ou expandir sua infraestrutura conforme necessário, mantendo abertas múltiplas opções para o futuro.</p>
]]></content:encoded></item><item><title><![CDATA[Como reforçar rapidamente a segurança da sua nova VPS com um Playbook Ansible simples!]]></title><description><![CDATA[Adquiri uma VPS para testes e, confesso, a preguiça de executar as configurações iniciais básicas, como criar um novo usuário ou definir autenticações, pode ser um grande vacilo. Quem nunca adiou um projeto por conta desses pequenos detalhes? Optar p...]]></description><link>https://blog.ricardotenv.dev/como-reforcar-rapidamente-a-seguranca-da-sua-nova-vps-com-um-playbook-ansible-simples</link><guid isPermaLink="true">https://blog.ricardotenv.dev/como-reforcar-rapidamente-a-seguranca-da-sua-nova-vps-com-um-playbook-ansible-simples</guid><category><![CDATA[ansible]]></category><category><![CDATA[ansible-playbook]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Mon, 18 Mar 2024 15:05:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/JJPqavJBy_k/upload/fde42c5be99dd9f002745774ef1587cf.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Adquiri uma VPS para testes e, confesso, a preguiça de executar as configurações iniciais básicas, como criar um novo usuário ou definir autenticações, pode ser um grande vacilo. Quem nunca adiou um projeto por conta desses pequenos detalhes? Optar pela rapidez, usando o usuário root com senha, pode parecer eficiente no início, mas é um risco. Imagine os testes evoluindo, o projeto sendo implementado e, de repente, sua máquina é invadida porque, hipoteticamente, uma aplicação desatualizada foi instalada com permissões de root. 😱 Aí a questão: como isso pôde acontecer? É por isso que, neste artigo, vou mostrar como automatizar essas "insignificantes" configurações iniciais e fortalecer a segurança de seus projetos.</p>
<h2 id="heading-conhecendo-um-pouco-do-ansible">Conhecendo um pouco do Ansible</h2>
<p>O <strong>Ansible</strong> é uma ferramenta de automação de TI desenvolvida para configurar sistemas, implantar software e orquestrar tarefas mais complexas de forma simplificada, utilizando a linguagem de marcação YAML <s>o primo bonito do HTML</s> que é bem simples. A ideia por trás de sua criação foi proporcionar uma solução de automação mais simples e acessível em comparação com as ferramentas existentes como <strong>Puppet</strong> e <strong>Chef</strong>. Ele opera de forma <em>agent-less</em>, significando que não requer instalação de software adicional nos sistemas que estão sendo gerenciados, comunicando-se com eles via SSH e o melhor, é desenvolvido em Python. <a target="_blank" href="https://emojipedia.org/yellow-heart">💛</a><a target="_blank" href="https://emojipedia.org/blue-heart">💙</a> 🐍</p>
<p>Este projeto está disponível no GitHub <a target="_blank" href="https://github.com/ricardotenv/SetupNewVPSWithAnsible">clicando aqui</a>, para você usar como uma ferramenta, mas se desejar replicá-lo localmente e aprender mais sobre <strong>Ansible</strong>, continue a leitura:</p>
<h2 id="heading-instalando-o-ansible">Instalando o Ansible</h2>
<p>Se você ainda não possui o <strong>Ansible</strong> instalado em sua máquina local, siga as etapas abaixo executando os comandos no seu terminal:</p>
<p>Instale o pacote <em>software-properties-common</em> para adicionar repositórios facilmente:</p>
<pre><code class="lang-bash">sudo apt install software-properties-common
</code></pre>
<p>Adicione o repositório PPA do <strong>Ansible</strong>:</p>
<pre><code class="lang-bash">sudo add-apt-repository ppa:ansible/ansible
</code></pre>
<p>Após adicionar o repositório, atualize a lista de pacotes:</p>
<pre><code class="lang-bash">sudo apt update
</code></pre>
<p>Agora você pode instalar o <strong>Ansible</strong>: 😀</p>
<pre><code class="lang-bash">sudo apt install ansible
</code></pre>
<p>Verifique se o <strong>Ansible</strong> foi instalado corretamente, exibindo a sua versão:</p>
<pre><code class="lang-bash">ansible --version
</code></pre>
<p>No Fedora é mais simples:</p>
<pre><code class="lang-bash">sudo dnf install ansible
</code></pre>
<h2 id="heading-a-estrutura-de-um-repositorio-ansible">A estrutura de um repositório Ansible</h2>
<p>A estrutura de um projeto em <strong>Ansible</strong> geralmente é direta. No nosso cenário, incluímos apenas três componentes: o arquivo <em>"hosts"</em> um arquivo <em>"readme"</em> com instruções sobre como utilizar o projeto e o <strong>playbook</strong>, que, na verdade, é um script <strong>Ansible</strong>.</p>
<pre><code class="lang-plaintext">setupvps/
├─ hosts
├─ playbook.yml
├─ README.md
</code></pre>
<h2 id="heading-criando-o-playbook-ansible">Criando o playbook Ansible</h2>
<p>Antes de iniciar o playbook, crie um arquivo de texto chamado "hosts" e insira o seguinte conteúdo nele:</p>
<pre><code class="lang-plaintext">[all]
# Substitua ESSA linha com o IP da sua VPS
</code></pre>
<p>O arquivo "hosts" no Ansible, normalmente chamado de inventário, desempenha um papel fundamental na definição dos nós ou "hosts" nos quais você deseja que o Ansible atue. Este arquivo identifica os nós de máquina, que podem ser servidores, desktops, entre outros, e os grupos de nós aos quais tarefas ou comandos Ansible serão direcionados.</p>
<p>No nosso exemplo, temos apenas um grupo de hosts definido: "all," e esse grupo inclui apenas um host. No entanto, é importante destacar que poderiam existir vários grupos ou vários hosts dentro desse grupo.</p>
<p>Agora, vamos finalmente criar o playbook. Começe pelo <strong>cabeçalho:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configurando</span> <span class="hljs-string">uma</span> <span class="hljs-string">nova</span> <span class="hljs-string">VPS</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">all</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Este trecho de script Ansible, intitulado "Configurando uma nova VPS", define uma tarefa que será executada em todos os hosts listados no inventário. O comando <strong>become: true</strong> indica que as tarefas serão executadas com privilégios elevados, geralmente como superusuário. Essa abordagem permite a configuração inicial da VPS com as ações necessárias para personalização e ajustes.</p>
<h2 id="heading-trabalhando-com-variaveis-no-ansible">Trabalhando com variáveis no Ansible</h2>
<p>No <strong>Ansible</strong>, as variáveis desempenham um papel crucial ao lidar com valores que podem variar a cada execução do playbook. Elas proporcionam uma forma de gerenciar dinamicamente as configurações e a execução do playbook, resultando em código mais reutilizável e flexível. As variáveis no <strong>Ansible</strong> podem ser definidas de diversas maneiras e em vários contextos, seja no playbook, nos arquivos de inventário, em arquivos de variáveis externas, ou até mesmo na linha de comando.</p>
<p><a target="_blank" href="https://blog.ricardotenv.dev/variaveis-no-ansible-aprenda-a-tirar-o-maximo-proveito-dessa-funcionalidade"><strong>Variáveis no Ansible: Aprenda a tirar o máximo proveito dessa funcionalidade</strong></a></p>
<p>Este comando cria uma variável que nos permite especificar o nome do usuário que desejamos criar na máquina. Esse usuário será configurado de forma segura e utilizado para operar a VPS.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">new_username:</span> <span class="hljs-string">"john_doe"</span>
</code></pre>
<p>O nome de usuário padrão é "John Doe," mas você pode alterá-lo para o nome de sua escolha. Além disso, lembre-se de que esse nome pode ser modificado a qualquer momento durante a execução do playbook, e vamos realizar isso facilmente no modo inline, diretamente pelo terminal, quando formos executar o playbook.</p>
<h2 id="heading-criando-tasks-no-ansible">Criando tasks no Ansible</h2>
<p>Agora vamos criar a primeira <strong>task</strong>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Criar</span> <span class="hljs-string">usuário</span> <span class="hljs-string">sem</span> <span class="hljs-string">senha</span>
      <span class="hljs-attr">user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ new_username }}</span>"</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">/bin/bash</span>
        <span class="hljs-attr">password:</span> <span class="hljs-string">""</span>
        <span class="hljs-attr">generate_ssh_key:</span> <span class="hljs-literal">yes</span>
</code></pre>
<p>Este trecho cria um usuário sem senha no sistema. O nome do usuário é fornecido pela variável <em>new_username</em> que criamos anteriormente, o shell é configurado como "/bin/bash", e uma chave SSH é gerada para permitir autenticação sem senha. Essa abordagem é útil quando a autenticação por chave SSH é preferida sobre senha e é bem mais seguro.</p>
<h2 id="heading-permitindo-comandos-em-sudo-sem-o-uso-de-senha">Permitindo comandos em sudo sem o uso de senha</h2>
<p>Neste bloco de tarefa, uma configuração é realizada para permitir que um usuário possa executar comandos <strong>sudo</strong> sem ser solicitado a fornecer uma senha.</p>
<pre><code class="lang-yaml">    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Permitir</span> <span class="hljs-string">sudo</span> <span class="hljs-string">sem</span> <span class="hljs-string">senha</span>
      <span class="hljs-attr">lineinfile:</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/etc/sudoers</span>
        <span class="hljs-attr">line:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ new_username }}</span> ALL=(ALL) NOPASSWD: ALL"</span>
        <span class="hljs-attr">validate:</span> <span class="hljs-string">"visudo -cf %s"</span>
      <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">become_user:</span> <span class="hljs-string">root</span>
</code></pre>
<h2 id="heading-ativando-autenticacao-ssh">Ativando autenticação SSH</h2>
<p>Este bloco incorpora uma chave SSH pública específica ao usuário definido, possibilitando que o usuário realize login ou execute comandos via SSH sem a necessidade de uma senha.</p>
<pre><code class="lang-yaml">    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Ativar</span> <span class="hljs-string">autenticação</span> <span class="hljs-string">SSH</span> <span class="hljs-string">para</span> <span class="hljs-string">o</span> <span class="hljs-string">usuário</span>
      <span class="hljs-attr">authorized_key:</span>
        <span class="hljs-attr">user:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ new_username }}</span>"</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
        <span class="hljs-attr">key:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ lookup('file', '~/.ssh/id_ed25519.pub') }}</span>"</span>
</code></pre>
<h2 id="heading-desabilitando-login-no-root">Desabilitando login no root</h2>
<p>Este bloco desativa o login SSH direto como usuário root. Utiliza o módulo "lineinfile" para modificar o arquivo de configuração do SSH, adicionando ou alterando a linha "PermitRootLogin no". Após a alteração, notifica o serviço SSH para reiniciar, assegurando que as mudanças entrem em vigor.</p>
<pre><code class="lang-yaml">   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Desativar</span> <span class="hljs-string">login</span> <span class="hljs-string">SSH</span> <span class="hljs-string">como</span> <span class="hljs-string">root</span>
      <span class="hljs-attr">lineinfile:</span>
        <span class="hljs-attr">path:</span> <span class="hljs-string">/etc/ssh/sshd_config</span>
        <span class="hljs-attr">regex:</span> <span class="hljs-string">'^#?PermitRootLogin'</span>
        <span class="hljs-attr">line:</span> <span class="hljs-string">'PermitRootLogin no'</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">notify:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">restart</span> <span class="hljs-string">ssh</span>
</code></pre>
<h2 id="heading-disparando-um-evento-no-ansible">Disparando um evento no Ansible</h2>
<pre><code class="lang-yaml">  <span class="hljs-attr">handlers:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">restart</span> <span class="hljs-string">ssh</span>
      <span class="hljs-attr">service:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">ssh</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">restarted</span>
</code></pre>
<p>O bloco <strong>handlers</strong> define ações que só são executadas se notificadas por outras tarefas. No contexto fornecido, ele especifica o que deve acontecer quando uma tarefa notifica o handler <em>restart ssh</em>.</p>
<p>Então, se uma tarefa no playbook fizer mudanças que necessitem de uma reinicialização do serviço SSH (por exemplo, a tarefa que altera o arquivo <em>/etc/ssh/sshd_config</em> para desativar o login do root), ela notificará esse handler, e o serviço SSH será reiniciado, aplicando as novas configurações.</p>
<p>Agora, vem à etapa mais esperada: a execução desse playbook por meio de um comando.</p>
<p>Antes de prosseguir, certifique-se de que a máquina na qual você pretende executar o playbook possui acesso à VPS. Caso o acesso seja feito por meio de senha, é recomendável adicionar sua chave SSH.</p>
<pre><code class="lang-bash">ansible-playbook playbook.yml -i hosts -u root -e <span class="hljs-string">"new_username=ricardotenv"</span>
</code></pre>
<p>Ao executar este comando, utilizamos o arquivo hosts como parâmetro de -i. Como há apenas um grupo no inventário, não é necessário especificar o nome do grupo. Além disso, fornecemos o nome de usuário com -u, que o <strong>Ansible</strong> usará para configurar a máquina. Como se trata de uma máquina nova, o usuário é root. Por fim, como mencionado anteriormente, especificamos o nome de usuário que desejamos criar, no meu caso, o meu próprio.</p>
]]></content:encoded></item><item><title><![CDATA[10 dicas valiosas para se manter como freelancer durante a busca por recolocação no mercado]]></title><description><![CDATA[O cenário atual no mercado de tecnologia tem sido marcado por mudanças significativas. Nos últimos meses, observei uma volta forçada ao trabalho presencial, vários layoffs e um aumento notável de profissionais anteriormente privilegiados, agora busca...]]></description><link>https://blog.ricardotenv.dev/10-dicas-valiosas-para-se-manter-como-freelancer-durante-a-busca-por-recolocacao-no-mercado</link><guid isPermaLink="true">https://blog.ricardotenv.dev/10-dicas-valiosas-para-se-manter-como-freelancer-durante-a-busca-por-recolocacao-no-mercado</guid><category><![CDATA[LinkedIn]]></category><category><![CDATA[freelancer]]></category><category><![CDATA[upwork]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Mon, 22 Jan 2024 16:39:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/h5xEHzfepNk/upload/83737278fcc7b1084799b069f795530d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O cenário atual no mercado de tecnologia tem sido marcado por mudanças significativas. Nos últimos meses, observei uma volta <s>forçada</s> ao trabalho presencial, vários <strong>layoffs</strong> e um aumento notável de profissionais anteriormente privilegiados, agora buscando oportunidades através do selo <strong>#OpenToWork</strong> no <strong>LinkedIn</strong>. Infelizmente, eu me encontro nesse grupo. Perdi meu emprego, me vi sem rumo, com aluguel para pagar e sem uma reserva de emergência.</p>
<p>Diante desse desafio, ativei meu modo de sobrevivência e mergulhei na batalha, oferecendo meus serviços como <strong>freelancer</strong>. Ao longo desse período, apliquei estratégias diversas, aprendi lições valiosas e decidi compartilhar minhas experiências neste post na esperança de auxiliar outras pessoas que enfrentam circunstâncias semelhantes e quero deixar claro que este post não visa ser um case de sucesso, mas sim ajudar aqueles que precisam "se virar".</p>
<p>Seguindo essas dicas, nos últimos meses, fiz uma entrega para um empreendedor indiano, e recebi em dólar. Pintaram duas propostas de sociedade, e ainda firmei um contrato de exclusividade. E, no mês de Dezembro, meu faturamento deu um salto, ultrapassando meu antigo salário trabalhando para uma empresa.</p>
<h2 id="heading-1-pare-de-reclamar-agora">1. Pare de reclamar agora!</h2>
<p>Então, você está em busca de uma recolocação no mercado e depara-se com posts no <strong>LinkedIn</strong> sobre o uso do selo <strong>#OpenToWork</strong>. Surgem dúvidas sobre se deve ou não usar, e no post seguinte, alguém reclama da <strong>Gupy</strong> e seu algoritmo não humanizado. Esse tipo de situação pode criar inseguranças, fazendo com que, sem perceber, você culpe o governo, os recém-chegados na sua área e até o recrutador da última entrevista que não deu feedback. É fácil cair nesse ciclo de atribuir a fase difícil a todos, sem tomar medidas para melhorar. Pior ainda, reclamar se torna satisfatório e viciante.</p>
<p>Lembre-se de todas as superações que te trouxeram até aqui, como um profissional preparado e dedicado que ama o que faz. Você enfrentou diversos desafios, e este momento é apenas uma fase. Você vai superar, então não precisa passar o tempo reclamando. Embora pareça discurso de coach, focar apenas nas reclamações realmente não ajuda.</p>
<h2 id="heading-2-aprenda-a-prospectar-clientes">2. Aprenda a prospectar clientes</h2>
<p>A maneira mais fácil e acessível de encontrar clientes é se cadastrando em plataformas de <strong>freelancers</strong>. O cliente apresenta o problema na plataforma, e você tem a oportunidade de oferecer seus serviços para resolvê-lo. Existem várias plataformas no Brasil e no mundo para isso. Deixo aqui o link das que já utilizei e recomendo:</p>
<ul>
<li><p><a target="_blank" href="https://www.upwork.com/">Upwork</a></p>
</li>
<li><p><a target="_blank" href="https://www.99freelas.com.br/invite/2079049">99Freelas</a></p>
</li>
</ul>
<h2 id="heading-3-utilize-o-chatgpt-para-desenvolver-sua-abordagem-e-vender-os-seus-servicos"><strong>3. Utilize o ChatGPT para desenvolver sua abordagem e vender os seus serviços</strong></h2>
<p><em>"As máquinas podem superar o homem, mas não podem superar o homem com outra máquina"</em>. Apesar de não saber quem é o autor, eu adoro essa frase.</p>
<p>Enviar propostas e se apresentar para clientes pode demandar muito tempo pois para conseguir alguma coisa você terá que enviar várias propostas. Eu tive que otimizar meu tempo utilizando o <strong>ChatGPT</strong>.</p>
<p>Nas plataformas de freelancer o clientes sempre colocam uma descrição do seu problema e serviço que precisam então eu pedi a ajuda do <strong>ChatGPT</strong>, disse para ele agir como um vendedor e aplicar o método <strong>AIDA</strong> em cima dos textos dos potenciais clientes que eu enviasse despertando o interesse e curiosidade deles e funcionou muito bem para mim.</p>
<h2 id="heading-4-call-estrategica"><strong>4. Call estratégica</strong></h2>
<p>Marque uma call para compreender os desafios do cliente. Demonstre empatia, mergulhe no negócio dele e pense em como suas habilidades podem impulsionar o crescimento. Neste momento tente focar no cliente, deixe-o à vontade e confiante de que você irá ajudá-lo.</p>
<h2 id="heading-5-cobre-justo-valorize-seu-trabalho-mas-nao-seja-ganancioso"><strong>5. Cobre justo: valorize seu trabalho mas não seja ganancioso</strong></h2>
<p>No início, é difícil atingir o valor de mercado. O que fiz foi calcular meus custos básicos, incluindo alimentação, internet, aluguel, água, luz, entre outros. Considerei também o <strong>DAS do MEI</strong> (se você não tem, abra um) e as taxas cobradas pelas plataformas de <strong>freelancers</strong>. Defini uma margem pequena de lucro para cobrir algumas regalias e, somando todos esses custos, dividi pelo valor da minha hora ou dia de trabalho. Em alguns casos, acrescento um percentual com base no risco e no retorno da entrega para o cliente, levando em consideração também o nível de gestão. Por exemplo, se o cliente possui pouco conhecimento na área, não segue uma metodologia definida e não tem uma visão clara do que deseja, minha taxa pode ser mais elevada. Por outro lado, se o projeto já conta com um especialista conduzindo, a taxa pode ser mais acessível. Em projetos nos quais o cliente está disposto a oferecer mais prazo, aplico um percentual de desconto. Essa abordagem permite que eu gerencie diferentes projetos de forma consecutiva, proporcionando uma maior flexibilidade na agenda.</p>
<h2 id="heading-6-melhore-suas-habilidades-de-comunicacao"><strong>6. Melhore suas habilidades de comunicação</strong></h2>
<p>Em muitos casos, os clientes podem ter dificuldade em expressar suas necessidades. Aprenda a fazer as perguntas certas, evitando termos técnicos, especialmente se você for um desenvolvedor.</p>
<h2 id="heading-7-construa-confianca"><strong>7. Construa confiança</strong></h2>
<p>Após fechar o serviço, mantenha uma comunicação diária com o cliente. Marque na agenda com ele um horário diário de até 15 minutos. Relate o progresso do projeto, e revise os requisitos, fortalecendo a confiança e demonstrando seu comprometimento.</p>
<h2 id="heading-8-tenha-controle-financeiro"><strong>8. Tenha controle financeiro</strong></h2>
<p>Trabalhar em uma empresa oferece a comodidade de saber exatamente quanto será o salário no próximo mês, permitindo gastos sem preocupações, desde que estejam dentro do limite salarial, e a empresa cuida dos descontos de impostos no holerite. No entanto, como <strong>freelancer</strong>, essa tranquilidade acaba. Você não tem certeza sobre quanto receberá e quanto terá disponível para despesas mensais. Portanto, é crucial assumir o controle das suas finanças, especialmente ao lidar com valores variáveis por projeto ou diárias. Aqui estão três dicas essenciais:</p>
<ol>
<li><p><strong>Pense como uma empresa:</strong> Considere tudo o que entra como seu faturamento mensal.</p>
</li>
<li><p><strong>Gastos Fixos Essenciais:</strong> Liste e priorize seus gastos fixos para sobreviver e trabalhar, como aluguel e mercado.</p>
</li>
<li><p><strong>Observe os indicadores:</strong> Crie indicadores para saber onde você gasta mais, ou qual projeto te dá mais retorno.</p>
</li>
</ol>
<p>Você pode aplicar essas três dicas utilizando uma planilha. Eu particularmente utilizo <a target="_blank" href="https://mobills.page.link/ZQup">uma ferramenta chamada Mobills</a>, onde registro meus ganhos e gastos. Essa ferramenta permite <strong>gerar gráficos</strong> e <strong>indicadores</strong>, além de capturar notificações no celular, o que facilita o registro das transações. Essa abordagem proporciona um maior <strong>controle financeiro</strong>, tornando a gestão de finanças mais eficiente.</p>
<h2 id="heading-9-descubra-seu-diferencial"><strong>9. Descubra seu diferencial</strong></h2>
<p>Percebi que muitos <strong>freelancers</strong> fazem ofertas muito baixas e depois abandonam projetos por propostas mais lucrativas. Então, criei meu diferencial baseando-se nisso: foco em soluções menos exploradas, que recebem menos propostas. Meu compromisso é o que me destaca, garanto a entrega para o cliente me baseando em <strong>metodologias Àgil</strong>, mas sem me aprofundar.</p>
<h2 id="heading-10-aprenda-a-treinar-pessoas"><strong>10. Aprenda a treinar pessoas</strong></h2>
<p>Você só tem 24 horas/dia e em algum momento você irá precisar escalar seus serviços e em algum lugar tem alguém que pode mudar de vida através do seu conhecimento.</p>
<h2 id="heading-boa-sorte">Boa sorte!</h2>
<p>É isso, pessoal! Tudo que compartilhei aqui foi testado e aprovado por mim. Escrevi com as melhores intenções, esperando que alcance e ajude alguém. Se você tiver dúvidas, precisar de mais dicas ou quiser deixar seu contato para possíveis trabalhos, é só me chamar no privado. Estamos juntos nessa jornada! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Variáveis no Ansible: Aprenda a tirar o máximo proveito dessa funcionalidade]]></title><description><![CDATA[Olá, entusiastas do Ansible!
Embora eu geralmente não me apegue a ferramentas devido a experiências passadas, o Ansible é uma exceção notável. A economia de tempo que essa ferramenta poderosa me proporcionou é imensurável, e posso afirmar que devo a ...]]></description><link>https://blog.ricardotenv.dev/variaveis-no-ansible-aprenda-a-tirar-o-maximo-proveito-dessa-funcionalidade</link><guid isPermaLink="true">https://blog.ricardotenv.dev/variaveis-no-ansible-aprenda-a-tirar-o-maximo-proveito-dessa-funcionalidade</guid><category><![CDATA[ansible]]></category><category><![CDATA[ansible-playbook]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Thu, 12 Oct 2023 22:19:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697147826067/f96a9cf5-a3aa-4798-9489-ecc2140154ea.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Olá, entusiastas do <strong>Ansible</strong>!</p>
<p>Embora eu geralmente não me apegue a ferramentas devido a experiências passadas, o <strong>Ansible</strong> é uma exceção notável. A economia de tempo que essa ferramenta poderosa me proporcionou é imensurável, e posso afirmar que devo a ela boa parte dos meus momentos produtivos e tempo de vida. 😅</p>
<p>Hoje vamos explorar o uso das variáveis no <strong>Ansible</strong> para tornar os nossos playbooks mais flexíveis e dinâmicos.</p>
<h3 id="heading-comecando-com-variaveis-basicas"><strong>Começando com Variáveis Básicas</strong></h3>
<p>Inicialmente, vamos entender a definição básica de uma variável no contexto do <strong>Ansible</strong>. O exemplo abaixo ilustra um playbook simples que define uma variável para criar um usuário.</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">your_target</span>
  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">user</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Criando</span> <span class="hljs-string">um</span> <span class="hljs-string">novo</span> <span class="hljs-string">usuário</span>
      <span class="hljs-attr">user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ username }}</span>"</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
</code></pre>
<p>Neste trecho, <em>username</em> é definido como <em>user</em> e é usado para criar um novo usuário em todos os hosts definidos em <em>your_target</em>.</p>
<h3 id="heading-expandindo-com-variaveis-externas"><strong>Expandindo com Variáveis Externas</strong></h3>
<p>Mas e se quisermos modificar o <em>username</em> sem alterar o playbook? As variáveis externas entram em cena para proporcionar essa flexibilidade:</p>
<pre><code class="lang-bash">ansible-playbook -i your_inventory_file playbook.yml -e <span class="hljs-string">"username=newuser"</span>
</code></pre>
<p>Aqui, o valor de <em>username</em> é sobrescrito para <em>newuser</em> durante essa execução específica do playbook, proporcionando um alto grau de reusabilidade para nossos scripts.</p>
<h3 id="heading-explorando-fatos-do-ansible"><strong>Explorando Fatos do Ansible</strong></h3>
<p><strong>Fatos</strong> ou <strong>Facts</strong> no Ansible são informações sobre os hosts remotos que o Ansible adquire antes de executar tarefas. Eles podem ser usados para implementar lógicas condicionais nos playbooks. Confira o exemplo a seguir:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">your_target</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Exibindo</span> <span class="hljs-string">o</span> <span class="hljs-string">hostname</span>
      <span class="hljs-attr">debug:</span>
        <span class="hljs-attr">msg:</span> <span class="hljs-string">"O hostname é <span class="hljs-template-variable">{{ ansible_hostname }}</span>"</span>
</code></pre>
<p>O valor <code>{{ ansible_hostname }}</code> é um <strong>fato</strong> que exibirá o nome do host durante a execução do playbook.</p>
<h3 id="heading-utilizando-registros"><strong>Utilizando Registros</strong></h3>
<p>O Ansible permite armazenar os resultados de uma tarefa em uma variável para usar em tarefas futuras. Vamos ver como isso funciona:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">your_target</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Criando</span> <span class="hljs-string">usuário</span>
      <span class="hljs-attr">user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">another_user</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">register:</span> <span class="hljs-string">user_creation_result</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Exibindo</span> <span class="hljs-string">resultado</span> <span class="hljs-string">da</span> <span class="hljs-string">criação</span> <span class="hljs-string">do</span> <span class="hljs-string">usuário</span>
      <span class="hljs-attr">debug:</span>
        <span class="hljs-attr">msg:</span> <span class="hljs-string">"A saída da tarefa anterior é: <span class="hljs-template-variable">{{ user_creation_result }}</span>"</span>
</code></pre>
<p>O output da tarefa de criação do usuário é armazenado na variável <em>user_creation_result</em> e pode ser usado em tarefas subsequentes.</p>
<p>Este post é dedicado a guiá-lo na utilização de variáveis no <strong>Ansible</strong>, garantindo que você esteja bem preparado para acompanhar minhas próximas publicações, que explorarão cenários mais práticos.</p>
<p>Referências:</p>
<p><a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html">https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html</a><br /><a target="_blank" href="https://spacelift.io/blog/ansible-variables">https://spacelift.io/blog/ansible-variables</a></p>
]]></content:encoded></item><item><title><![CDATA[Como obter um IP brasileiro em um servidor VPS sem gastar muito]]></title><description><![CDATA[É inegável que os impostos no Brasil podem encarecer significativamente os serviços de Datacenter, pois a manutenção no país uma é opção bem mais "salgada" que em outros. Como consequência, os provedores muitas vezes repassam esses custos para os ser...]]></description><link>https://blog.ricardotenv.dev/como-obter-um-ip-brasileiro-em-um-servidor-vps-sem-gastar-muito</link><guid isPermaLink="true">https://blog.ricardotenv.dev/como-obter-um-ip-brasileiro-em-um-servidor-vps-sem-gastar-muito</guid><category><![CDATA[vpn]]></category><category><![CDATA[vps]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Wed, 19 Jul 2023 15:35:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0mfj0jJt0dY/upload/24a5ab2e353fd8cc5445f7b67a087e5e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>É inegável que os impostos no Brasil podem encarecer significativamente os serviços de Datacenter, pois a manutenção no país uma é opção bem mais "salgada" que em outros. Como consequência, os provedores muitas vezes repassam esses custos para os serviços de VPS e infraestrutura de hospedagem no Brasil, resultando em preços elevados para os clientes. A vantagem de optar por uma VPN é que seu servidor não precisa necessariamente estar localizado no território brasileiro.</p>
<p>Configurar uma VPN em um servidor VPS pode parecer uma tarefa desafiadora para muitos, mas não precisa ser uma dor de cabeça. Com algumas etapas simples, você pode ter seu servidor teoricamente no Brasil, pelo menos é o que as pessoas vão pensar 😅.</p>
<p>Este artigo parte do pressuposto de que o leitor já conhece os termos <strong>VPS</strong> e <strong>VPN</strong>, porém, mesmo assim, farei um breve resumo. Uma VPS é uma instância de um servidor físico, enquanto uma VPN é um software que protege sua conexão através de criptografia, assegurando a segurança do tráfego de dados e a privacidade.</p>
<h2 id="heading-por-que-usamos-uma-vpn-nessa-solucao">Por que usamos uma VPN nessa solução?</h2>
<p>Uma VPN pode ser utilizada em diversos cenários e pode ser facilmente substituída por um servidor proxy, caso seja necessário por exemplo fazer requisições em um site que permita apenas IP brasileiro. Embora pudesse ter optado por um proxy, alguns fatores externos me impediram de fazê-lo. No meu caso eu precisava de uma solução completa de disparos de E-mail, que obrigatoriamente demandava um IP brasileiro. Neste post, vamos focar no uso da VPN para alterar o IP da rede do servidor.</p>
<h2 id="heading-instalando-e-configurando-uma-vpn-no-linux-com-openvpn-e-nordvpn">Instalando e configurando uma VPN no Linux com OpenVPN e NordVPN</h2>
<p>Vamos ao que realmente interessa! Primeiro, escolha um servidor VPS confiável. Neste caso, utilizaremos o serviço da <strong>NordVPN</strong> e uma VPS com <strong>Ubuntu 22</strong>, e estarei logado na máquina via <strong>SSH</strong>.</p>
<p>Abaixo, eu fiz uma requisição para o site <a target="_blank" href="http://ipinfo.io">ipinfo.io</a> com o <strong>cURL</strong>. Acho esse site muito interessante, pois ele reconhece que é uma requisição <em>Headless</em> e me responde com um JSON, como o exemplo abaixo, onde é possível visualizar o IP e a localização da minha VPS:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"ip"</span>: <span class="hljs-string">"143.198.69.202"</span>,
  <span class="hljs-attr">"city"</span>: <span class="hljs-string">"Santa Clara"</span>,
  <span class="hljs-attr">"region"</span>: <span class="hljs-string">"California"</span>,
  <span class="hljs-attr">"country"</span>: <span class="hljs-string">"US"</span>,
  <span class="hljs-attr">"loc"</span>: <span class="hljs-string">"37.3924,-121.9623"</span>,
  <span class="hljs-attr">"org"</span>: <span class="hljs-string">"AS14061 DigitalOcean, LLC"</span>,
  <span class="hljs-attr">"postal"</span>: <span class="hljs-string">"95054"</span>,
  <span class="hljs-attr">"timezone"</span>: <span class="hljs-string">"America/Los_Angeles"</span>,
  <span class="hljs-attr">"readme"</span>: <span class="hljs-string">"https://ipinfo.io/missingauth"</span>
}
</code></pre>
<p>Agora vamos iniciar a instalação da VPN na máquina, obviamente vamos precisar de um cliente VPN, e vamos usar o OpenVPN.</p>
<p>Primeiro faça a instalação do OpenVPN:</p>
<pre><code class="lang-bash">sudo apt update
sudo apt-get install openvpn
</code></pre>
<p>Entre na pasta de configuração do OpenVPN e faça o download dos arquivos de configuração, a vantagem aqui é que a NordVPN já oferece esses arquivos prontos.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /etc/openvpn &amp;&amp; sudo wget https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip
</code></pre>
<p>Descompacte o arquivo baixado e, caso não tenha o <strong>unzip</strong> instalado, instale-o com o comando abaixo:</p>
<pre><code class="lang-bash">sudo apt-get install unzip
</code></pre>
<p>E finalmente faça a extração dos arquivos:</p>
<pre><code class="lang-bash">sudo unzip ovpn.zip
</code></pre>
<p>Ao acessar o conteúdo baixado, você encontrará duas pastas: "ovpn_tcp" e "ovpn_udp". Para este caso específico, vamos utilizar o protocolo <strong>TCP</strong>, presente na primeira pasta.</p>
<h2 id="heading-escolhendo-um-ip-brasileiro">Escolhendo um IP brasileiro</h2>
<p>Para obter o endereço IP que você precisa, basta abrir o link: <a target="_blank" href="https://nordvpn.com/pt-br/servers/tools/">https://nordvpn.com/pt-br/servers/tools/</a>. Neste site, eles irão te recomendar um servidor, mas você também pode escolher a localização que mais lhe convém.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689190265356/2d265979-28f1-4d5c-b30e-4af1fe8add6e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-configurando-a-vps">Configurando a VPS</h2>
<p>Antes de prosseguir, é importante fazer algumas configurações na rede da VPS. Caso contrário, você corre o risco de perder a conexão e seu terminal poderá travar, deixando-o preso do lado de fora, incapaz de realizar qualquer ação. Nessa situação, a única solução seria reiniciar sua VPS através do console do provedor.</p>
<p><img src="https://media.giphy.com/media/2WxWfiavndgcM/giphy.gif" alt class="image--center mx-auto" /></p>
<p>Para evitar esse problema, execute os seguintes comandos em sua VPS:</p>
<pre><code class="lang-bash">sudo apt-get install net-tools ipcalc
</code></pre>
<p>Esse comando é fundamental para instalar duas ferramentas importantes que serão essenciais para seguir os próximos passos.</p>
<p>Este comando adiciona uma regra de roteamento na tabela 128 para pacotes originados do endereço IP principal da máquina e também cria uma rota padrão na tabela 128, utilizando o gateway padrão.</p>
<pre><code class="lang-bash">sudo ip rule add from $(hostname -I | awk <span class="hljs-string">'{print $1}'</span>) table 128 &amp;&amp; \
sudo ip route add table 128 default via $(ip route show default | awk <span class="hljs-string">'/default/ {print $3}'</span>)
</code></pre>
<p>Agora vamos pegar a subnet mask para também incluí-la nas configurações:</p>
<pre><code class="lang-bash">ipcalc $(ip addr show dev eth0 | awk <span class="hljs-string">'/inet / {print $2}'</span> | head -n 1) | awk -F<span class="hljs-string">': '</span> <span class="hljs-string">'/Netmask/ {print $2}'</span> | head -n 1
</code></pre>
<p>Esse comando mostrará uma saída como essa:</p>
<pre><code class="lang-bash">255.255.240.0 = 20   11111111.11111111.1111 0000.00000000
</code></pre>
<p>Então minha subnet mask é <code>255.255.240.0/20</code>.</p>
<p>Agora adicione...</p>
<pre><code class="lang-bash">sudo ip route add table 128 to 255.255.240.0/20 dev eth0
</code></pre>
<h2 id="heading-conectando-se-na-vpn">Conectando-se na VPN</h2>
<p>Agora copie o endereço fornecido pela <strong>NordVPN</strong>, adicione o protocolo e .ovpn na frente e execute o arquivo de configuração com o <strong>OpenVPN</strong>.</p>
<pre><code class="lang-bash">sudo openvpn --config br53.nordvpn.com.tcp.ovpn --daemon
</code></pre>
<p>Nesse exemplo, estamos executando o arquivo de configuração da <strong>NordVPN</strong>, mas poderia ser um arquivo de outro provedor ou do seu próprio servidor <strong>VPN</strong>.</p>
<h3 id="heading-autenticando-se-na-vpn">Autenticando-se na VPN</h3>
<p>Reparou que foi solicitado usuário e senha para autenticação? Vamos obter essas credenciais diretamente na <strong>NordVPN</strong>.</p>
<p>Clique no menu lateral esquerdo e vá para "<strong>Serviços</strong>" ➡️ "<strong>NordVPN</strong>" ➡️ "<strong>Configurar a NordVPN manualmente</strong>". Faça a verificação usando o código enviado para o seu e-mail e, finalmente, encontre suas credenciais de usuário e senha.</p>
<p>Está pronto. 🚀 Agora ao executar o comando <code>curl ipinfo.io</code> você terá uma saída confirmando a localização.</p>
<pre><code class="lang-bash">{
  <span class="hljs-string">"ip"</span>: <span class="hljs-string">"177.54.151.190"</span>,
  <span class="hljs-string">"city"</span>: <span class="hljs-string">"São Paulo"</span>,
  <span class="hljs-string">"region"</span>: <span class="hljs-string">"São Paulo"</span>,
  <span class="hljs-string">"country"</span>: <span class="hljs-string">"BR"</span>,
  <span class="hljs-string">"loc"</span>: <span class="hljs-string">"-23.5475,-46.6361"</span>,
  <span class="hljs-string">"org"</span>: <span class="hljs-string">"AS262287 Latitude.sh LTDA"</span>,
  <span class="hljs-string">"postal"</span>: <span class="hljs-string">"01000-000"</span>,
  <span class="hljs-string">"timezone"</span>: <span class="hljs-string">"America/Sao_Paulo"</span>,
  <span class="hljs-string">"readme"</span>: <span class="hljs-string">"https://ipinfo.io/missingauth"</span>
}
</code></pre>
<h2 id="heading-resumo">Resumo</h2>
<p>Certamente! É importante lembrar que essa é uma solução para problemas específicos, e dependendo das circunstâncias, podem ser necessárias configurações adicionais para atender às suas necessidades específicas.</p>
<p>Compartilhar experiências e conhecimentos é fundamental para aprimorar soluções e ajudar uns aos outros. Se você conhece uma solução melhor ou se essa solução funcionou para você, compartilhe nos comentários! Isso pode beneficiar outros usuários e enriquecer a discussão.</p>
<p>Referências:</p>
<p><a target="_blank" href="https://support.nordvpn.com/Connectivity/Linux/1047409422/Connect-to-NordVPN-using-Linux-Terminal.htm">https://support.nordvpn.com/Connectivity/Linux/1047409422/Connect-to-NordVPN-using-Linux-Terminal.htm</a></p>
]]></content:encoded></item><item><title><![CDATA[Como gerenciar privilégios de usuários no MongoDB]]></title><description><![CDATA[E aí, Devs! Vamos falar sobre MongoDB, o banco de dados não relacional que é simplesmente incrível? Eu tive a oportunidade de trabalhar com ele e aprendi algumas dicas valiosas para ajudar a garantir a segurança dos dados.
O MongoDB é uma ferramenta ...]]></description><link>https://blog.ricardotenv.dev/como-gerenciar-privilegios-de-usuarios-no-mongodb</link><guid isPermaLink="true">https://blog.ricardotenv.dev/como-gerenciar-privilegios-de-usuarios-no-mongodb</guid><category><![CDATA[MongoDB]]></category><category><![CDATA[mongo]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Tue, 11 Jul 2023 14:11:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/lRoX0shwjUQ/upload/556384159b888c1f317f142d06dfffbe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>E aí, Devs! Vamos falar sobre MongoDB, o banco de dados não relacional que é simplesmente incrível? Eu tive a oportunidade de trabalhar com ele e aprendi algumas dicas valiosas para ajudar a garantir a segurança dos dados.</p>
<p>O <strong>MongoDB</strong> é uma ferramenta poderosa, mas como todo banco de dados, ele precisa ser configurado corretamente para garantir que os dados sejam protegidos adequadamente. Algumas das medidas que aprendi incluem a configuração adequada de senhas fortes e a utilização de autenticação para evitar acessos não autorizados.</p>
<p>Mas hoje vou focar em um aspecto importante do <strong>MongoDB</strong>: a <strong>gestão de usuários e permissões/privilégios</strong>. Essa prática é fundamental para garantir o controle de acesso e a segurança dos dados.</p>
<h2 id="heading-e-como-posso-aplicar-isso-no-meu-dia-a-dia">E como posso aplicar isso no meu dia a dia?</h2>
<p>Imagine uma <strong>EdTech</strong> fictícia que utiliza uma instância do <strong>Mongo</strong> como seu banco de dados principal para armazenar informações de alunos e servir dados em sua ferramenta de <strong>Business Intelligence (BI)</strong>.</p>
<p>Nesse caso, quem terá acesso a esses dados será a própria <strong>ferramenta de BI</strong>, um <strong>administrador de banco de dados (DBA)</strong> ou assim como eu, um <strong>DevOps</strong> e o <strong>painel de controle</strong> que alimenta e controla esses dados.</p>
<p>Primeiro vamos se logar ao banco, estou considerando que estamos montando um novo banco de dados porém esse tutorial também pode servir para bancos já existentes, então vamos do princípio. Nessa caso iremos usar uma instância do tipo standalone, mas também serve para clusters. Após ter uma máquina com o Mongo instalado, vamos fazer o primeiro acesso. Execute o camando abaixo em seu terminal:</p>
<pre><code class="lang-javascript">mongo --host localhost
</code></pre>
<h2 id="heading-criando-um-usuario-no-mongodb">Criando um usuário no MongoDB</h2>
<p>Vamos executar o comando abaixo debtro do Mongo para criar o nosso primeiro usuário que será o root:</p>
<pre><code class="lang-javascript">use admin
db.createUser({
    <span class="hljs-attr">user</span>: <span class="hljs-string">"root"</span>,
    <span class="hljs-attr">pwd</span>: <span class="hljs-string">"password"</span>,
    <span class="hljs-attr">roles</span>: [
        { <span class="hljs-string">"role"</span>: <span class="hljs-string">"root"</span>, <span class="hljs-string">"db"</span>: <span class="hljs-string">"admin"</span> }
    ]
})
</code></pre>
<p>Este comando cria um usuário com privilégios de "root" (superusuário) no banco de dados "admin".</p>
<p>O comando começa selecionando o banco de dados "admin" com o comando "use admin". Em seguida, é utilizado o método "createUser" para criar um novo usuário com nome de usuário "username" e senha "password". A propriedade "roles" é usada para especificar as funções ou papéis que o usuário terá no banco de dados.</p>
<p>No exemplo acima, o usuário tem apenas uma função, que é o papel de "root" no banco de dados "admin". Isso significa que o usuário terá privilégios de superusuário para realizar tarefas administrativas, como criar e excluir bancos de dados, criar e excluir usuários e configurar as configurações do servidor.</p>
<p>Suponhamos que este usuário foi o primeiro a ser criado imediatamente após a implementação do banco de dados, com o objetivo de garantir que operações não sejam realizadas sem autenticação, e que este usuário será atribuído ao nosso <strong>DBA</strong> ou <strong>DevOps</strong>, mas tome muito cuidado esse usuário tem os privilégios máximos e a senha deve ser armazenada em um local seguro.</p>
<p>Essa conta root será guardada para momentos críticos e pouquíssimas pessoas terão aceosso. Criei um usuário de administrador chamado "lossMan"<strong>,</strong> não se importe com a escolha do nome, mas tenha em mente que eu não usei algo como "admin" apenas para dificultar a vida de "crackers" que usam bot de força bruta ou algo do tipo. Também não se preocupe por enquanto com a role que eu dei para esse usuário, falaremos disso mais à frente.</p>
<pre><code class="lang-javascript">use admin
db.createUser(
  {
    <span class="hljs-attr">user</span>: <span class="hljs-string">"myUserAdmin"</span>,
    <span class="hljs-attr">pwd</span>: <span class="hljs-string">"password"</span>,
    <span class="hljs-attr">roles</span>: [
      { <span class="hljs-attr">role</span>: <span class="hljs-string">"userAdminAnyDatabase"</span>, <span class="hljs-attr">db</span>: <span class="hljs-string">"admin"</span> },
      { <span class="hljs-attr">role</span>: <span class="hljs-string">"readWriteAnyDatabase"</span>, <span class="hljs-attr">db</span>: <span class="hljs-string">"admin"</span> }
    ]
  }
)
</code></pre>
<h2 id="heading-ativando-a-autenticacao-no-mongodb">Ativando a autenticação no MongoDB</h2>
<p>Para garantir a segurança do seu banco de dados, é importante configurar o Mongo para aceitar apenas acesso por autenticação. Se você está seguindo este tutorial em um banco de dados existente, pode pular esta etapa.</p>
<p>Para começar, abra o arquivo de configuração do Mongo e faça as alterações necessárias para permitir apenas o acesso autenticado:</p>
<pre><code class="lang-bash">sudo nano /etc/mongod.conf
</code></pre>
<p>Adicione a seguinte linha ao arquivo de configuração</p>
<pre><code class="lang-yaml"><span class="hljs-attr">security:</span>
    <span class="hljs-attr">authorization:</span> <span class="hljs-string">enabled</span>
</code></pre>
<p>Essa linha de configuração garante que o Mongo só permitirá o acesso autenticado ao banco de dados, proporcionando maior segurança aos dados. Certifique-se de salvar as alterações no arquivo e reiniciar o serviço do Mongo para que as mudanças tenham efeito.</p>
<h2 id="heading-autenticando-se-no-mongodb">Autenticando-se no MongoDB</h2>
<p>Agora, o acesso ao seu banco de dados só será possível através da autenticação com o usuário criado anteriormente. O usuário "lossMan" se autentica na base "admin" e tem os seguintes privilégios:</p>
<ul>
<li><p><strong>userAdminAnyDatabase</strong>: Permite que o usuário crie, adicione ou remova roles, bem como crie e modifique roles personalizadas para qualquer base de dados.</p>
</li>
<li><p><strong>readWriteAnyDatabase</strong>: Permite que o usuário faça modificações em qualquer base de dados.</p>
</li>
</ul>
<p>Tendo isso em mente irel logar com meu usuário administrador:</p>
<pre><code class="lang-bash">mongo --host localhost -u lossMan --authenticationDatabase admin -p
</code></pre>
<p>Agora que já me autentiquei no banco de dados podemos criar a base da "edetch" e os usuários que terão acesso.</p>
<p>Para começar, vamos criar um usuário para a ferramenta de Business Intelligence com acesso restrito ao tipo "somente leitura":</p>
<pre><code class="lang-javascript">use admin
db.createUser({
    <span class="hljs-attr">user</span>: <span class="hljs-string">"dataAnalysis"</span>,
    <span class="hljs-attr">pwd</span>: <span class="hljs-string">"password"</span>,
    <span class="hljs-attr">roles</span>: [
        { <span class="hljs-string">"role"</span>: <span class="hljs-string">"read"</span>, <span class="hljs-string">"db"</span>: <span class="hljs-string">"edtech"</span> }
    ]
})
</code></pre>
<p>Por questões de segurança, o usuário foi criado na base "admin", mas seu acesso está restrito apenas à base "edtech" e com permissão somente de leitura. Caso ele tente realizar operações de escrita nessa base, receberá uma mensagem de erro.</p>
<p>Por fim, criamos o usuário "controlPanel" com a role "readWrite", que permite que ele leia e escreva na base "edtech".</p>
<h2 id="heading-adicionando-privilegios-a-um-usuario-no-mongodb">Adicionando privilégios a um usuário no MongoDB</h2>
<p>Suponha que, por algum motivo, o usuário "dataAnalysis" da ferramenta de BI precise de privilégios de escrita para registrar na nossa base e, talvez, cadastrar métricas de alunos no banco para que sejam visualizadas pelos próprios alunos. Para conceder a ele permissão de escrita na base, precisaremos adicionar o privilégio de escrita.</p>
<pre><code class="lang-javascript">use admin
db.grantRolesToUser(
    <span class="hljs-string">"dataAnalysis"</span>,
    [
      { <span class="hljs-attr">role</span>: <span class="hljs-string">"readWrite"</span>, <span class="hljs-attr">db</span>: <span class="hljs-string">"edtech"</span> }
    ]
)
</code></pre>
<p>Execute o seguinte comando para verificar os privilégios que esse usuário agora possui:</p>
<pre><code class="lang-javascript">db.getUser(<span class="hljs-string">"dataAnalysis"</span>)
</code></pre>
<p>A saída será como essa:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"_id"</span> : <span class="hljs-string">"admin.dataAnalysis"</span>,
    <span class="hljs-attr">"user"</span> : <span class="hljs-string">"dataAnalysis"</span>,
    <span class="hljs-attr">"db"</span> : <span class="hljs-string">"admin"</span>,
    <span class="hljs-attr">"roles"</span> : [
        {
            <span class="hljs-attr">"role"</span> : <span class="hljs-string">"readWrite"</span>,
            <span class="hljs-attr">"db"</span> : <span class="hljs-string">"edtech"</span>
        },
        {
            <span class="hljs-attr">"role"</span> : <span class="hljs-string">"read"</span>,
            <span class="hljs-attr">"db"</span> : <span class="hljs-string">"edtech"</span>
        }
    ],
    <span class="hljs-attr">"mechanisms"</span> : [
        <span class="hljs-string">"SCRAM-SHA-1"</span>,
        <span class="hljs-string">"SCRAM-SHA-256"</span>
    ]
}
</code></pre>
<p>Observe que agora temos privilégios redundantes, pois ambas as roles concedem privilégios de leitura na base. Portanto, vamos remover a role de somente leitura.</p>
<h2 id="heading-revogando-privilegios-no-mongodb">Revogando privilégios no MongoDB</h2>
<p>O próximo comando irá remover a role de "somente leitura" deixando apenas a role de "leitura e escrita":</p>
<pre><code class="lang-javascript">db.revokeRolesFromUser(
    <span class="hljs-string">"dataAnalysis"</span>,
    [
      { <span class="hljs-attr">role</span>: <span class="hljs-string">"read"</span>, <span class="hljs-attr">db</span>: <span class="hljs-string">"edtech"</span> }
    ]
)
</code></pre>
<p>Pronto! Agora execute novamente o comando <code>db.getUser()</code> para garantir que a redundância foi eliminada:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"_id"</span> : <span class="hljs-string">"admin.dataAnalysis"</span>,
    <span class="hljs-attr">"user"</span> : <span class="hljs-string">"dataAnalysis"</span>,
    <span class="hljs-attr">"db"</span> : <span class="hljs-string">"admin"</span>,
    <span class="hljs-attr">"roles"</span> : [
        {
            <span class="hljs-attr">"role"</span> : <span class="hljs-string">"readWrite"</span>,
            <span class="hljs-attr">"db"</span> : <span class="hljs-string">"edtech"</span>
        }
    ],
    <span class="hljs-attr">"mechanisms"</span> : [
        <span class="hljs-string">"SCRAM-SHA-1"</span>,
        <span class="hljs-string">"SCRAM-SHA-256"</span>
    ]
}
</code></pre>
<p>Se esta for a saída, então está tudo certo. Neste caso, utilizamos a revogação de privilégios apenas para eliminar a redundância, mas este comando pode ser usado para controlar acessos temporários e outras situações semelhantes.</p>
<p>Se você é um desenvolvedor ou está começando uma empresa, a segurança dos dados é fundamental. Nesse sentido, o uso de técnicas como a atribuição de privilégios em usuários MongoDB pode ser uma ótima maneira de garantir a integridade das informações.</p>
<p>Embora este post se concentre nas <a target="_blank" href="https://www.mongodb.com/docs/manual/reference/built-in-roles/">Built-In roles</a>, é importante lembrar que a utilização dessas roles é um caminho para um controle ainda mais refinado de privilégios. Não deixe de conferir futuras postagens do blog pois falarei sobre as roles customizadas, para aqueles que desejam ter um controle de privilégios mais avançado.</p>
<p>Referências:</p>
<p><a target="_blank" href="https://www.mongodb.com/docs/manual/tutorial/manage-users-and-roles">https://www.mongodb.com/docs/manual/tutorial/manage-users-and-roles/#modify-access-for-an-existing-user</a></p>
<p><a target="_blank" href="https://www.mongodb.com/docs/manual/reference/built-in-roles/">https://www.mongodb.com/docs/manual/reference/built-in-roles/</a></p>
<p><a target="_blank" href="https://www.mongodb.com/docs/manual/tutorial/configure-scram-client-authentication/">https://www.mongodb.com/docs/manual/tutorial/configure-scram-client-authentication/</a></p>
]]></content:encoded></item><item><title><![CDATA[Estilizando seu terminal Linux com o Gogh e o Oh My Bash]]></title><description><![CDATA[Esqueça a Matrix, vamos dar um toque de cor ao seu terminal, assim como os fichários e cadernos da época escolar. Mas não se preocupe, ainda podemos dar aquele visual "hackudão" com aquelas letras verdes clássicas, para você fazer um "apt update" e i...]]></description><link>https://blog.ricardotenv.dev/estilizando-seu-terminal-linux-com-o-gogh-e-o-oh-my-bash</link><guid isPermaLink="true">https://blog.ricardotenv.dev/estilizando-seu-terminal-linux-com-o-gogh-e-o-oh-my-bash</guid><category><![CDATA[Linux]]></category><category><![CDATA[linux for beginners]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Sun, 09 Jul 2023 02:59:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4Mw7nkQDByk/upload/b7bebcd74131c00ac23e53aa90a887c8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Esqueça a Matrix, vamos dar um toque de cor ao seu terminal, assim como os fichários e cadernos da época escolar. Mas não se preocupe, ainda podemos dar aquele visual "hackudão" com aquelas letras verdes clássicas, para você fazer um "apt update" e impressionar seus amigos dizendo que está invadindo a NASA.</p>
<p>Preparado para dar um toque especial ao seu terminal? Vamos explorar as ferramentas <strong>Oh My Bash</strong> e <strong>Gogh</strong> para personalizar o prompt de comando e deixá-lo cheio de cores. Neste tutorial, iremos nos concentrar no <strong>Bash</strong>, o interpretador shell mais popular, e no gerenciador de pacotes <strong>APT</strong>. No entanto, fique à vontade para adaptar as instruções para outros interpretadores e gerenciadores de pacotes. Vamos começar a transformação do seu terminal e deixá-lo único e incrível!</p>
<p>Para não prolongar demais este post, vou resumir a explicação sobre essas ferramentas. Com o <a target="_blank" href="https://ohmybash.nntoan.com/"><strong>Oh My Bash</strong></a>, você poderá personalizar o prompt de comando do seu terminal, deixando-o com a sua cara. Já com o <a target="_blank" href="https://gogh-co.github.io/Gogh/"><strong>Gogh</strong></a>, você poderá colorir o fundo e as fontes do console, trazendo mais vida e estilo ao seu ambiente de trabalho.</p>
<h2 id="heading-instalando-o-oh-my-bash">Instalando o Oh My Bash</h2>
<p>Podemos utilizar o Wget ou o cURL, duas ferramentas populares para transferência de dados via linha de comando mas nesse tutorial eu irei utilizar o cURL, escolha a que mais lhe agrada e vamos começar!</p>
<p>Execute o seguinte comando para instalar o Oh My Bash:</p>
<pre><code class="lang-bash">bash -c <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/ohmybash/oh-my-bash/master/tools/install.sh)</span>"</span>
</code></pre>
<p>O OMB substitui seu arquivo .bashrc por um arquivo template e modifica a variável PS1 com o tema padrão.</p>
<p>Observe a saída do comando no meu terminal e repare na mudança do prompt:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688863934409/b99901a1-cc0f-4329-93b1-6d6d760e7149.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-alterando-o-tema-do-oh-my-bash">Alterando o tema do Oh My Bash</h2>
<p>Como ainda não está com o estilo ideal para o meu gosto, vamos buscar por um tema no link abaixo:</p>
<p><a target="_blank" href="https://github.com/ohmybash/oh-my-bash/wiki/Themes">https://github.com/ohmybash/oh-my-bash/wiki/Themes</a></p>
<p>Eu testei vários temas e escolhi o <strong>rr</strong>, para alterar o tema abra o arquivo .bashrc, encontre a variável OS_THEME que fica nas primeiras linhas do arquivo e substitua o seu valor com o nome do tema escolhido.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Set name of the theme to load. Optionally, if you set this to "random"</span>
<span class="hljs-comment"># it'll load a random theme each time that oh-my-bash is loaded.</span>
OSH_THEME=<span class="hljs-string">"rr"</span>
</code></pre>
<p>Para surgir efeito execute o comando abaixo:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.bashrc
</code></pre>
<p>Pronto! Com o OMB você também pode instalar plugins para git, linguagens de programação e frameworks, mas falarei sobre isso em um outro dia.</p>
<h2 id="heading-instalando-o-gogh">Instalando o Gogh</h2>
<p>Agora que temos o nosso prompt personalizado, vamos dar cor ao terminal!</p>
<pre><code class="lang-bash">bash -c  <span class="hljs-string">"<span class="hljs-subst">$(curl -sLo- https://git.io/vQgMr)</span>"</span>
</code></pre>
<p>Ao executar o comando, o <strong>Gogh</strong> irá listar todos os temas de cores disponíveis e solicitará que você digite o valor correspondente ao tema escolhido. No meu caso, eu escolhi o tema Wryan, que possui o número 242. Você pode conferir todos os temas disponíveis no link abaixo:</p>
<p><a target="_blank" href="https://gogh-co.github.io/Gogh/">https://gogh-co.github.io/Gogh/</a></p>
<p>Caso surja algum problema com o dconf, uma solução simples é reiniciar o terminal. Além disso, para ativar o tema escolhido na configuração de perfil do terminal, você pode seguir as instruções no gif abaixo:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688868221492/7dbc6c6a-1bbd-493a-b9ce-942a2ac23345.gif" alt class="image--center mx-auto" /></p>
<p>Lembrando que o <strong>Gogh</strong> instala um pacote de cores que pode ser personalizado de acordo com suas preferências então você terá que configurar manualmente as cores da fonte.</p>
<p>Eu fiz algumas alterações no meu e o resultado final foi esse.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688879113319/295b1929-9ed4-403f-82af-685c8d7c093f.png" alt class="image--center mx-auto" /></p>
<p>Este tutorial tem como objetivo auxiliar iniciantes em Linux a superarem o receio da linha de comando, pois entendemos que, no início, uma tela preta com diversos comandos pode parecer assustadora e confusa.</p>
]]></content:encoded></item><item><title><![CDATA[Adicionando um domínio personalizado na Vercel]]></title><description><![CDATA[Pra quem ainda não conhece a Vercel e já conhece a Netlify não tenho muito o que dizer, as duas fazem basicamente a mesma coisa, hospedam sites estáticos com deploy automático. Eu achei a interface da segunda mais bonita mas a primeira é mais fácil d...]]></description><link>https://blog.ricardotenv.dev/adicionando-um-dominio-personalizado-na-vercel</link><guid isPermaLink="true">https://blog.ricardotenv.dev/adicionando-um-dominio-personalizado-na-vercel</guid><category><![CDATA[web]]></category><category><![CDATA[Vercel]]></category><dc:creator><![CDATA[Ricardo Santos]]></dc:creator><pubDate>Mon, 05 Apr 2021 03:01:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/OqtafYT5kTw/upload/87d7db9c55f81697bdd6d51f8b58a1e6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Pra quem ainda não conhece a <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> e já conhece a <a target="_blank" href="https://www.netlify.com/"><strong>Netlify</strong></a> não tenho muito o que dizer, as duas fazem basicamente a mesma coisa, hospedam sites estáticos com deploy automático. Eu achei a interface da segunda mais bonita mas a primeira é mais fácil de “deployar”, porém as diferenças são mínimas, eu escolhi a <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> para servir esse blog simplesmente porque está na moda. 🤷‍♂️</p>
<p>O registrar desse tutorial será o <a target="_blank" href="https://domains.google/intl/pt-BR_br/"><strong>Google Domains</strong></a> pois foi onde registrei esse meu domínio, na época se bem me lembro somente o Google registrava domínios .dev, o processo é muito simples, mas mesmo assim vou tentar facilitar mais ainda.</p>
<h2 id="heading-adicionando-seu-dominio-proprio">Adicionando seu domínio próprio</h2>
<p>Assim que fazemos um deploy na <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> recebemos um endereço completamente aleatório para acessarmos nosso site, não queremos isso, queremos acessar através do nosso domínio bonitinho 🥰, para isso precisamos dentro da plataforma da <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> fazer os seguintes passos:</p>
<h3 id="heading-1-selecione-seu-projeto">➡️ 1. Selecione seu projeto</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671768856630/a0640738-4a08-45b5-8de3-1bf9dffdcf1f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-navegue-para-settings">➡️ 2. Navegue para <code>Settings</code></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671768860080/2ca6b76e-19cf-40c1-b232-791a78813bc5.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-selecione-o-item-domains-no-menu-ao-canto-esquerdo">➡️ 3. Selecione o ítem <code>Domains</code> no menu ao canto esquerdo</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671768879309/5d63edf5-1f9d-48d8-ab69-9e5f75d369ad.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-4-adicione-seu-dominio">➡️ 4. Adicione seu domínio</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671768888233/26967431-17a7-4c31-a600-e14316785a90.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-5-configure-o-redirecionamento">➡️ 5. Configure o redirecionamento</h3>
<p>Você verá um pop-up como esse:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671768912155/14410a81-b839-408b-8e48-8cfae435a7c8.png" alt class="image--center mx-auto" /></p>
<p>No meu caso como eu quero que o domínio sem o alias <strong>www</strong> seja o padrão do meu site eu escolhi a segunda opção, você também pode escolher a terceira opção e decidir isso mais tarde.</p>
<h3 id="heading-6-configurando-os-nameservers">➡️ 6. Configurando os nameservers</h3>
<p>Você verá uma tela como essa:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671769779607/183d73d8-a8d5-44c6-b6d3-4a26f83caa3a.png" alt class="image--center mx-auto" /></p>
<p>clique na aba <code>Nameservers</code> e copie os endereços fornecidos.</p>
<p>Caso seu domínio seja novo você verá algo parecido com isso:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671769788855/c7112c11-d7a2-4c86-9f54-c8ee799d4860.png" alt class="image--center mx-auto" /></p>
<p>clique em <strong>Usar servidores personalizados de nome</strong>. Cole no campo os dois endereços fornecidos pela <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> e clique em <strong>Salvar</strong>.</p>
<h3 id="heading-conclusao">➡️ Conclusão</h3>
<p>Foi tudo muito simples né? Depois desses passos você vai perceber que a <a target="_blank" href="https://vercel.com/"><strong>Vercel</strong></a> fica atualizando a verificação do seu domínio, quando você perceber a frase <strong>Valid Configuration</strong> significa que deu tudo certo e seu domínio está configurado, possa ser que ele demore um pouco para propagar o que é comum, o meu levou várias horas para propagar no Brasil, eu usei a ferramenta <a target="_blank" href="http://whatsmydns.net"><strong>whatsmydns.net</strong></a> para fazer as verificações, mas não precisa ser ansioso como eu, deixa rolar, se você seguiu todos os passos em breve seu site estará no ar com o seu domínio próprio.</p>
<p><img src="https://media.giphy.com/media/CPyK0LpgSUytcTX8Bm/giphy.gif" alt class="image--center mx-auto" /></p>
<p><strong>Referências</strong>:</p>
<ul>
<li><a target="_blank" href="https://vercel.com/docs/custom-domains#"><strong>Custom Domains - Vercel Documentation</strong></a></li>
</ul>
]]></content:encoded></item></channel></rss>