Minhas Anotações

Aqui você encontra alguns dos meus Gists públicos.
Tem de tudo, desde configurações de projetos até scripts que facilitam meu dia-a-dia.

Como criar alias de comandos no terminal Windows


Como criar alias (apelidos) de comandos no windows para aumentar a produtividade

Por exemplo, para executar um projeto React Native no Android, temos que executar o comando npx react-native run-android. Fica muito mais fácil digitar apenas ra

No CMD, se executarmos o comando

doskey ra=npx react-native run-android $*

podemos rodar o projeto no Android simplesmente digitando

ra

Porém, após fechar o CMD, o alias não funciona mais, é preciso criá-lo novamente... :grimacing:

Pra resolver isso é possível criar um arquivo init.cmd, por exemplo, em uma pasta bats no disco C, com o seguinte conteúdo:

@echo off
doskey ra=npx react-native run-android $*

Pra quem usa Windows Terminal

No arquivo settings.json do Windows Terminal, substitui o comando "commandline": "cmd.exe", por "commandline": "cmd.exe /K C:\\bats\\init.cmd",

Pra quem só usa CMD

É possível criar um atalho na área de trabalho e definir o "Destino" como sendo C:\Windows\System32\cmd.exe /K C:\\bats\\init.cmd

E então abrir o CMD sempre a partir desse atalho (ou defini-lo como terminal externo no VSCode)


Se alguém quiser saber mais:

https://thecloudblog.net/post/save-your-precious-dev-time-with-command-aliases-in-windows-terminal/

Teste técnico para desenvolvedores React.JS


Construa um componente de dropdown

Nesse teste gostaríamos que você construisse um componente dropdown reutilizável. O dropdown deve abrir ao clicar no ícone "mais", e deve exibir algumas ações extras para que o usuário possa interagir.

Estado fechado:

<img width="400" alt="image" src="https://cdn.discordapp.com/attachments/693151307116314736/1001195614320742540/134912453-ea7f0734-cbbd-4768-ba66-8e3aa78a357c.png">

Estado Aberto:

<img width="401" alt="image" src="https://cdn.discordapp.com/attachments/693151307116314736/1001195638609936405/134908637-27ed1d8f-e041-45f3-b1d6-cac54b7ebfaf.png">

O ícone "mais" e alguns valores de cores estão incluídos abaixo, mas não se preocupe muito em combinar perfeitamente com o design. Estes foram fornecidos para poupar tempo na escolha de cores e iconografia.

Coisas para se pensar

  • Devemos permitir que o menu seja alinhado à esquerda e à direita?
  • O componente deve ser reutilizável, então tente pensar na API que você fornecerá a outros desenvolvedores
  • O componente funcionará para usuários que não possuem mouse?

Uma nota sobre a estrutura

Apesar de ficar totalmente a seu critério como o componente será estruturado, nós gostaríamos que você utilizasse o conceito de composição ao invés de herança para esse teste (saber mais). Nós não vamos lhe desclassificar caso opte por utilizar um ou outro, mas como vários projetos nossos já estão utilizando composição, é meio que "a preferência da casa" 😄

// composição
<Tile>
  <TileHeader>Header</TileHeader>
  <TileContent>Conteúdo...</TileContent>
  <TileFooter>
    <SecondaryButton onClick={...}>Cancelar</SecondaryButton>
    <PrimaryButton onClick={...}>Prontinho!</PrimaryButton>
  </TileFooter>
<Tile>

// herança
<Tile
  header="Header"
  content="Conteúdo..."
  onCancelClick={...}
  onDoneClick={...}
/>

Pode ser útil

// backgrounds
black100: hsla(225, 14%, 12%, 1)
black200: hsla(225, 14%, 14%, 1)
black300: hsla(225, 14%, 16%, 1)
black400: hsla(225, 14%, 18%, 1)
black500: hsla(225, 14%, 20%, 1)
black600: hsla(225, 14%, 22%, 1)
black700: hsla(225, 14%, 24%, 1)
black800: hsla(225, 14%, 26%, 1)
black900: hsla(225, 14%, 28%, 1)

// text
white100: hsla(225, 14%, 100%, 1)
white200: hsla(225, 14%, 100%, 0.8)

// primary
blue100: hsla(210, 100%, 37%, 1)
blue200: hsla(210, 100%, 33%, 1)
blue300: hsla(210, 100%, 35%, 1)
blue400: hsla(210, 100%, 33%, 0.5)

import React from "react";

type Props = {
  color?: string;
  width?: number | string;
  height?: number | string;
  size?: number | string;
};

const MoreIcon = ({ color, size = 24, ...props }: Props) => (
  <svg
    width={size}
    height={size}
    viewBox="0 0 24 24"
    fill="none"
    {...props}
  >
    <path
      fillRule="evenodd"
      clipRule="evenodd"
      d="M10.5 16.5a1.5 1.5 0 103 0 1.5 1.5 0 00-3 0zm0-4.5a1.5 1.5 0 103 0 1.5 1.5 0 00-3 0zm0-4.5a1.5 1.5 0 103 0 1.5 1.5 0 00-3 0z"
      fill={color || "currentColor"}
    />
  </svg>
);

export { MoreIcon };

Mão na massa!

  • Você deve gastar no máximo 3 horas para concluir esta tarefa. Não esperamos um código pronto para produção, mas pedimos que você forneça notas junto com sua URL de envio sobre o que você faria se tivesse mais tempo.
  • Aqui nós presamos muito por uma boa documentação, então não economize!
  • Conclua seu envio em um local compartilhável. Recomendamos usar https://codesandbox.io/.
  • Depois de concluído, envie por e-mail a url do seu envio para o(a) entrevistador(a) junto com quaisquer notas que ache válido)
  • TypeScript e testes unitários são opcionais (mas muito bem-vindos 😉)

Boa sorte, você consegue! 👊🏼

Configurar nginx pm2 node via ssh


Como configurar uma API Node Express via SSH

Aluguei um servidor da letscloud.io para testar os serviço, e como a configuração inicial foi bem complexa, irei deixar o passo a passo registrado aqui.

NOTA:

  1. Caso o servidor tenha sido criado na Hostinger, o passo "Configuração inicial da máquina remota" não é necessário
  2. Neste caso também, deve-se seguir primeiramente o tutorial de Como Configurar VPS em 5 Passos Simples

Configuração inicial da máquina remota

  1. Acesse o painel da Let's Cloud e crie uma nova instância de máquina, com Ubuntu 20.04 LTS
  2. Garanta que o provedor de domínio está apontando para os name servers da LetsCloud (mostrados abaixo nos registros NS)
  3. Acesse a aba DNS Any Cast e adicione os seguintes registros:
TypeNameValueTTL
Ayourdomain.com.brip.da.maquina3600
Awwwip.da.maquina3600
CNAMEautodiscoverautodiscoverredirection.letscloud.io3600
NSyourdomain.com.brns1.letscloud.io86400
NSyourdomain.com.brns2.letscloud.io86400
NSyourdomain.com.brns3.letscloud.io86400
NSyourdomain.com.brns4.letscloud.io86400
TXTyourdomain.com.brv=spf1 include:spf.letscloud.io~all 3600

Acesse o terminal via SSH

  1. No git bash, execute o comando
ssh usuario@ip.da.maquina
  1. Aceitar fingerprint
  2. Colar senha de acesso

Intalando o NodeJS 12

  1. Faça download do Node 12 via terminal
curl -sL https://deb.nodesource.com/setup_12.x -o nodesource_setup.sh
  1. Execute o script de configuração baixado
sudo bash nodesource_setup.sh
  1. Efetive a instalação do Node
sudo apt install nodejs
  1. Instale os pacotes necessários para compilar o Node
sudo apt install build-essential
  1. Crie um servidor Express de teste
nano hello.js

cole o conteúdo de teste

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})
  1. Instale o pm2
sudo npm install pm2@latest -g
  1. Execute o app de teste com pm2
pm2 start hello.js
  1. Teste no localhost da máquina remota se é possível realizar requisições ao app de teste
curl http://localhost:3000
  1. Configure o pm2 para iniciar junto ao boot do sistema
pm2 startup systemd
  1. Instale o nginx
sudo apt update
sudo apt install nginx
  1. Crie os arquivos de configuração do nginx
sudo ufw app list

sudo ufw status

sudo ufw allow 'Nginx HTTP'

sudo ufw status

systemctl status nginx

sudo mkdir -p /var/www/your_domain/html

sudo chown -R $USER:$USER /var/www/your_domain/html

sudo chmod -R 755 /var/www/your_domain

nano /var/www/your_domain/html/index.html

// cole o conteúdo do html

<html>
    <head>
        <title>Welcome to your_domain!</title>
    </head>
    <body>
        <h1>Success!  The your_domain server block is working!</h1>
    </body>
</html>

// crie um arquivo de configuração separado

sudo nano /etc/nginx/sites-available/your_domain

// cole o conteúdo

server {
        listen 80;
        listen [::]:80;

        root /var/www/your_domain/html;
        index index.html index.htm index.nginx-debian.html;

        server_name your_domain www.your_domain;

        location / {
                try_files $uri $uri/ =404;
        }
}

// crie um link para o arquivo

sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/

// descomente a diretiva #server_names_hash_bucket_size, tirando o #

sudo nano /etc/nginx/nginx.conf

...
http {
    ...
    server_names_hash_bucket_size 64;
    ...
}
...

sudo nginx -t

sudo systemctl restart nginx
  1. Crie um proxy reverso da porta 3000 que possa ser acessada externamente
sudo nano /etc/nginx/sites-available/your_domain

Cole o conteúdo do arquivo

server {
        listen 80;
        listen [::]:80;
        server_name www.yourdomain.com.br;
        return 301 $scheme://yourdomain.com.br$request_uri;
}

server {
        listen 80;
        listen [::]:80;

        root /var/www/yourdomain.com.br/html;
        index index.html index.htm index.nginx-debian.html;

        server_name yourdomain.com.br;

        location /apiemoutraporta {
        proxy_pass http://127.0.0.1:5504;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

        location / {
        proxy_pass http://127.0.0.1:5503;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Nota: /apiemoutraporta poderia ser um segundo processo que roda na porta 5504 e é acessado através da url: www.yourdomain.com.br/apiemoutraporta. Nessa segunda API você precisa configurar o express para responder às rotas da mesma forma:

app.use("/apiemoutraporta/api/", routes);

app.use(
  "/apiemoutraporta/api/media",
  express.static(
    path.join(
      __dirname,
      process.env.MODE === "development" ? "./dist/media" : "./media"
    )
  )
);

app.use("/apiemoutraporta/.well-known", express.static(path.join(__dirname, "./.well-known")));

app.use("/apiemoutraporta/inicio", express.static(path.join(__dirname, "./landing")));

app.use("/apiemoutraporta/", express.static(path.join(__dirname, "./admin")));

app.get("/apiemoutraporta/*", (req, res) => {
  res.sendFile(path.join(`${__dirname}/admin/index.html`));
});
  1. Verifique se a configuração do nginx está OK
sudo nginx -t
  1. Reinicie o nginx
sudo systemctl restart nginx

NOTA: Caso ocorra alguma erro para iniciar o nginx no Ubuntu 20.04, possivelmente é porque o Apache está utilizando a porta 80 Para resolver, basta desabilitaar/remover o Apache: https://www.cyberciti.biz/faq/how-do-i-stop-apache-from-starting-on-linux/

Clone o repositório do projeto

  1. Crie token de acesso do Github

  2. Configure seu usuário git na máquina remota

git config user.name --global "Gustavo-Kuze"

git config user.email --global "gustavoksilva3@hotmail.com"

git config -l

git clone https://github.com/yourdomain/restaurant-api.git

Nota: Quando solicitado pela senha no terminal, cole Token de acesso pessoal do Github

  1. Instale as dependências do seu projeto
npm i -g yarn

yarn

nano .env
  1. Faça o build do projeto
yarn build
  1. Inicie o projeto com pm2
pm2 start ./dist/index.js -n api

Configurando HTTPS (SSL)

  1. Instalar as dependências do cert bot
sudo apt install certbot python3-certbot-nginx
  1. Habilite o tráfego HTTPS no nginx
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'
  1. Obtenha o certificado Let's Encrypt via cert bot
sudo certbot --nginx -d yourdomain.com.br -d www.yourdomain.com.br
  1. Entre com o e-mail do responsável
  2. Concorde com os termos
  3. Selecione a opção para redirecionar todo o tráfego HTTP para HTTPS
  4. Em teoria está tudo certo e seu site já pode ser acesso de maneira segura com HTTPS! 🥳

DICA: O certificado Let's Encrypt só é válido por um período de 90 dias, e precisa ser atualizado para que o HTTPS continue funcionando. O Cert bot possui a funcionalidade de auto atualização e ela pode ser verificada da seguinte maneira:

Rode o comando para verificar se o timer está ativo:

sudo systemctl status certbot.timer

O output deve ser algo semelhante à:

● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Mon 2020-05-04 20:04:36 UTC; 2 weeks 1 days ago
    Trigger: Thu 2020-05-21 05:22:32 UTC; 9h left
   Triggers: ● certbot.service

É também possível simular o processo de atualização automática de certificado, executando o seguinte comando:

sudo certbot renew --dry-run

O resultado deve ser algo semelhante à:

Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/yourdomain.com.br.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator nginx, Installer nginx
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for yourdomain.com.br
http-01 challenge for www.yourdomain.com.br
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed with reload of nginx server; fullchain is
/etc/letsencrypt/live/yourdomain.com.br/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/yourdomain.com.br/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.

Configurando a sincronização do repositório automática

É possível realizar o git pull e instalação de dependências do projeto automaticamente (com yarn, por exemplo) da seguinte maneira:

  1. Crie um repositório vazio na raiz do seu servidor
git init --bare ~/projeto.git
  1. Crie um post-receive hook do git
nano /projeto.git/hooks/post-receive

cole

#!/bin/bash
TARGET="/pasta/de/deploy"
GIT_DIR="/projeto.git"
BRANCH="main"

while read oldrev newrev ref
do
        # only checking out the master (or whatever branch you would like to deploy)
        if [ "$ref" = "refs/heads/$BRANCH" ];
        then
                echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
                git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
        else
                echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
        fi
done

cd /pasta/de/deploy && yarn
pm2 restart api
  1. Torne-o executável
chmod +x post-receive
  1. Adicione o repositório remoto ao seu git local
git remote add production root@ip-da-maquina-remota:projeto.git
  1. dê um git push e veja seu código sendo atualizado como em um passe de mágica 🤯
git push production main

Como habilitar o FTP para editar os arquivos remotos de maneira mais fácil

https://phoenixnap.com/kb/install-ftp-server-on-ubuntu-vsftpd

Referências utilizadas

https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-20-04-pt

https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04

https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04

https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04

https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04

https://gist.github.com/noelboss/3fe13927025b89757f8fb12e9066f2fa#file-post-receive

Como resolver o erro "zsh compinit: insecure directories" no Mac OS X


Problema

O erro seguinte erro aparece ao abrir um novo terminal no Mac

zsh compinit: insecure directories, run compaudit for list.
Ignore insecure directories and continue [y] or abort compinit [n]?

Solução

  • Execute o comando: compinit
  • Para cada diretório printado no console, execute os dois seguintes comandos:
sudo chown $(whoami) PATH_HERE

sudo chmod -R 755 PATH_HERE

Por exemplo, se o resultado do print comando compinit forem os seguintes diretórios:

/usr/local/share/zsh
/usr/local/share/zsh/site-functions

Você deve executar a seguinte sequência de comandos:

sudo chown $(whoami) /usr/local/share/zsh

sudo chmod -R 755 /usr/local/share/zsh

sudo chown $(whoami) /usr/local/share/zsh/site-functions

sudo chmod -R 755 /usr/local/share/zsh/site-functions

Como depurar o Analytics no React Native Firebase


iOS

  1. No XCode, acesse a tela "Edit Scheme" (menu -> Product -> Scheme -> Edit Scheme...)
  2. Na aba Arguments, passe a flag "-FIRDebugEnabled" para habilitar o debug do Firebase
  3. Passe a flag "-FIRDebugDisabled" para desabilitar o debug.

Android

  1. Para habilitar o debug, execute o comando:

adb shell setprop debug.firebase.analytics.app com.meuapp.massa

  1. Para desabilitar, execute o comando:

adb shell setprop debug.firebase.analytics.app .none.

Invocar um script Python no NodeJS


Para invocar um script Python dentro do NodeJS, basta utilizar o spawn do child_process

Node

const { spawn } = require('child_process');
const path = require('path');
const util = require('util');

const pythonProcess = spawn(
  'python',
  [path.resolve(__dirname, './rename_dirs.py')],
  {
    cwd: 'D:\\python',
  },
);

pythonProcess.stdout.on('data', (data) => {
  const textChunk = data.toString('utf8'); // buffer to string

  util.log(textChunk);
});

Python

import os
import sys

def main():
    print('Enviar dados para o NodeJS')
    sys.stdout.flush()

if __name__ == '__main__':
    main()

NOTAS:

  1. Caso queira passar parâmetros para o script Python, basta passar mais strings no array (segundo parametro do método spawn)
  2. Para que a textChunk receba a saída do código python, no script rename_dirs.py, é necessário executar o comando sys.stdout.flush() após o print('enviar dados para o NodeJS')

Como criar TypeScript decorators


O que são decorators?

No TypeScript é possível criarmos funções que alteram o comportamento de outras funções (ou classes), sem que tenhamos que alterar a implementação original da mesma. Essas funções são chamadas decorators

Um decorator pode ser declarado da seguinte maneira:

export default function decoratorFunction(logInSeconds: boolean = false) {
  return function (
    target: any, // contexto onde o decorator está inserido
    propertyKey: string, // Nome do método
    descriptor: PropertyDescriptor // description.value é o método original
  ) {
    const originalFunction = descriptor.value;

    // aqui podemos sobrescrever o comportamento do método original
    descriptor.value = function (...args: any[]) {
      const result = originalFunction.apply(this, args);

    /*
        aqui devolvemos o mesmo retorno do método original
        à partir da chamada do decorator
    */
      return result;
    };

    return descriptor; // é necessário retornar o próprio descriptor ao final
  };
}

Exemplo de decorator real

export default function logProcessingTime(logInSeconds: boolean = false) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalFunction = descriptor.value;

    descriptor.value = function (...args: any[]) {
      let divisor = logInSeconds ? 1000 : 1;
      let unidade = logInSeconds ? "seconds" : "milliseconds";

      console.log("-----------------------");
      console.log(`${propertyKey} function params: ${JSON.stringify(args)}`);

      const initialTime = performance.now();

      const result = originalFunction.apply(this, args);
      console.log(`${JSON.stringify(result)} function result:`);

      const finalTime = performance.now();

      console.log(
        `${propertyKey} took ${(finalTime - initialTime) / divisor} ${unidade} to be processed`
      );
      console.log("-----------------------");

      return result;
    };
    return descriptor;
  };
}

Como utilizar o decorator logProcessingTime

Agora basta importar o decorator em qualquer módulo do node que quisermos, e decorar nossa função com ele. Isso fará com que o tempo de execução da função seja mostrado no console do navegador.

Ex:

import logProcessingTime from './decorators/logProcessingTime';

@logProcessingTime()
async function listarUsuariosAsync() {
    // Exemplo de chamada muito demorada à nossa API Rest
    return API.get('/users');
}

// ........

const users = await listarUsuariosAsync(); // adiciona took 1.580000011017546ms to be processed

Como configurar deeplinks no React Native


Introdução

O que é deeplink? 🤓

Quando o usuário clicar em um link do site em seu smartphone, uma tela do app é aberta ao invés do navegador (Chrome, Safari, etc). Isso é "deep linking".


Android

Configurando o servidor

Para que app seja associado ao website, é preciso criar uma arquivo assetlinks.json no diretório .well-known do website, de tal forma que seja possível acessá-lo assim:

https://www.ololo.com/.well-known/assetlinks.json

Dentro do arquivo é preciso informar o package_name e a hash SHA-256. Exemplo de arquivo (dados fakes):

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "br.com.ololo",
    "sha256_cert_fingerprints": ["B1:A3:8A:C9:3A:4B:32:92:FF:53:6A:EB:00:32:E9:D6:B8:2A:B1:D3:A3:57:CC:A8:28:A1:1F:AD:94:92:A7:8C"]
  }
}]

Atente-se aos seguintes detalhes (extraídos da documentação oficial):

  • O arquivo assetlinks.json deve ser exibido com tipo de conteúdo application/json.
  • O arquivo assetlinks.json precisa ser acessível por meio de uma conexão HTTPS, independentemente de os filtros de intent do app declararem HTTPS como o esquema de dados ou não.
  • O arquivo assetlinks.json precisa ser acessível sem redirecionamentos (sem redirecionamentos 301 ou 302) e ser acessível por bots. O robots.txt precisa permitir o rastreamento de /.well-known/assetlinks.json.
  • Se os links do app forem compatíveis com vários domínios de host, publique o arquivo assetlinks.json em cada domínio. Consulte Como oferecer compatibilidade com links de app para diversos hosts.
  • Não publique seu app com URLs de desenvolvimento/teste em arquivos de manifesto que podem não ser publicamente acessíveis (por exemplo, todos aqueles que só podem ser acessados com uma VPN). Uma solução alternativa nesses casos é configurar variantes de compilação para gerar outro arquivo de manifesto para versões de desenvolvimento.

Configurando o App

No arquivo AndroidManifest.xml, adicione os caminhos do site que devem ser tratados pelo app:

 <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
        android:screenOrientation="portrait"
        android:windowSoftInputMode="adjustResize"
        android:exported="true"
        android:launchMode="singleTask">
        <!-- android:autoVerify="true" é obrigatorio -->
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />

            <!-- defina os caminhos que devem ser tratados aqui: -->

          <data android:scheme="https" android:host="www.ololo.com" android:pathPattern="/ololo/produto/.*/p-.*" />
          <data android:scheme="https" android:host="www.ololo.com" android:pathPattern="/ololo/promocoes" />/>


        </intent-filter>
        <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="ololo" />
        </intent-filter>
      </activity>

Testes

É possível testar se o arquivo está devidamente configurado através da seguinte ferramenta: https://developers.google.com/digital-asset-links/tools/generator?hl=pt-br

Para mais informações relacionada à configuração do servidor no Android, favor acessar a documentação oficial: https://developer.android.com/training/app-links/verify-site-associations?hl=pt-br#request-verify


iOS

Configurando o servidor

Para que app seja associado ao website, é preciso criar uma arquivo apple-app-site-association na raiz do site, ou no diretório .well-known, de tal forma que seja possível acessá-lo assim:

https://www.ololo.com/apple-app-site-association

Exemplo de arquivo (dados fakes):

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "7VCZVTXU85.br.com.ololo",
        "paths": [
          "/ololo/promocoes",
          "/ololo/produto/*/p-*"
        ]
      }
    ]
  },
  "webcredentials": {
    "apps": [
      "7VCZVTXU85.br.com.ololo"
    ]
  }
}

Atente-se aos seguintes detalhes:

  • O arquivo assetlinks.json precisa ser acessível por meio de uma conexão HTTPS, independentemente de os filtros de intent do app declararem HTTPS como o esquema de dados ou não.
  • O arquivo assetlinks.json precisa ser acessível sem redirecionamentos (sem redirecionamentos 301 ou 302) e ser acessível por bots. O robots.txt precisa permitir o rastreamento de /.well-known/assetlinks.json.
  • O arquivo apple-app-site-association precisa ser servido com o header "application/pkcs7-mime"

Segue um exemplo de como servir o header "application/pkcs7-mime" no Node JS:

var aasa = fs.readFileSync(__dirname + '/static/apple-app-site-association');
app.get('/apple-app-site-association', function(req, res, next) {
  res.set('Content-Type', 'application/pkcs7-mime');
  res.status(200).send(aasa);
});

Configurando o app

  1. No arquivo AppDelegate.m, adicione as seguintes instruções:
#import <React/RCTLinkingManager.h>

...

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}

Isto irá transmitir a URL para o código React Native

  1. Crie um arquivo de entitlements (ex: ololo.entitlements), que indica a qual domínio seu site pertence. Exemplo:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>development</string>
	<key>com.apple.developer.associated-domains</key>
	<array>
		<string>applinks:ololo.com</string>
		<string>applinks:www.ololo.com</string>
	</array>
</dict>
</plist>

Testes

É possível testar se o arquivo está devidamente configurado através da ferramenta: https://limitless-sierra-4673.herokuapp.com/

Para mais informações sobre como configurar o servidor no ios, favor acessar a documentação oficial: https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html

Como acessar os links no React Native

A URL clicada estará disponível no React Native através do método Linking.getInitialURL. Exemplo:

Linking.getInitialURL().then((url) => {
    console.log(`Deeplink clicado: ${url}` )
});

Executar múltiplos túneis NGROK


Para configurar o ngrok para rodar tanto o projeto frontend quanto o backend simultaneamente, basta adicionar o seguinte código ao arquivo "C:\Users\USER\.ngrok2\ngrok.yml"

authtoken: 1bGvuFnqnXjihKbkqtx8qVld2ie_2736j4KfrDdMHhBh3uDtn
tunnels:
  portafront:
    proto: http
    addr: 8000
  portaapi:
    proto: http
    addr: 8888

E então ao invés de executar apenas um tunnel com o comando ngrok http 8000, por exemplo, basta executar todos os túneis definidos nesse arquivo, com:

ngrok start --all

Erro de cache no NPM


Solução para o erro de cache que ocorre no NPM para usuário Windows com espaços no nome

Executar no cmd:

npm config set cache "C:\Users\Gustavo~1Kuze\AppData\Roaming\npm-cache" --global

NOTE: Se você estiver utilizando o NVM, o CMD deve ser executado como administrador

Erro ao criar chave estrangeira MySQL


Mysql error 1452 - Cannot add or update a child row: a foreign key constraint fails

Para resolver esse problema, basta desabilitar a verificação de FKs antes de adicionar a nova referência, por exemplo:

SET foreign_key_checks = 0; // executar isso para desabilitar

/*Adicionar a foreign key aqui, ou cria manualmente no workbench*/

SET foreign_key_checks = 1; // executar isso para habilitar novamente

Como configurar o Prettier/ESLint com o preset do AirBnb em projetos React e NodeJS


Como configurar o Prettier/ESLint com o preset do AirBnb em projetos React e NodeJS

ReactJS

  1. Instale as seguintes dependencias em seu projeto React:
yarn add -D babel-eslint prettier eslint-config-prettier eslint-plugin-prettier eslint eslint-plugin-react eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import eslint-import-resolver-webpack
  1. Crie um arquivo .eslintrc.json na raiz do projeto com o seguinte conteúdo:
{
  "extends": ["airbnb", "prettier", "prettier/react"],
  "parser": "babel-eslint",
  "plugins": ["react", "prettier"],
  "rules": {
    "react/jsx-filename-extension": [
      1,
      {
        "extensions": [".js", "jsx"]
      }
    ],
    "prettier/prettier": "error",
    "max-len": ["error", 80],
    "react/prop-types": 0
  },
  "env": {
    "browser": true
  },
  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [".js", ".jsx", ".ts", ".tsx"],
        "paths": ["src"]
      }
    }
  }
}
  1. Adicione esse script ao seu package.json:

"lint": "eslint src -c .eslintrc.json --ext js,jsx",

  1. Crie um arquivo .prettierrc.json, na raiz do projeto, com o seguinte conteúdo:
  {
    "singleQuote": true,
    "trailingComma": "all"
  }

NodeJS

  1. Instale as seguintes dependencias em seu projeto Node:
yarn add -D eslint eslint-config-airbnb-base eslint-plugin-import eslint-plugin-promise prettier-eslint-cli
  1. Crie um arquivo .eslintrc.json na raiz do projeto com o seguinte conteúdo:
{
    "env": {
        "node": true
    },
    "extends": "airbnb-base",
    "plugins": ["promise"],
    "rules": {
        "no-console": "warn",
        "promise/always-return": "error",
        "promise/no-return-wrap": "error",
        "promise/param-names": "error",
        "promise/catch-or-return": "error",
        "promise/no-native": "off",
        "promise/no-nesting": "error",
        "promise/no-promise-in-callback": "error",
        "promise/no-callback-in-promise": "error",
        "promise/no-return-in-finally": "error",
        "prefer-arrow-callback": "error",
        "linebreak-style": "off"
    }
}
  1. Adicione esses scripts ao seu package.json:
"lint": "eslint 'src/**/*.js' --fix",
"format": "prettier-eslint 'server/**/*.{js,json}' --write"

Creating keystore and getting SHA-1 React Native


Go to keytool's location

cd "C:\Program Files\Java\jdk1.8.0_211\bin"

Generate keystore

keytool -genkeypair -v -keystore MYKEYSTORE.keystore -alias MY-ALIAS -keyalg RSA -keysize 2048 -validity 10000

Get SHA-1

keytool -list -v -keystore PATH_TO_YOUR_KEYSTORE.keystore -alias YOUR_ALIAS_NAME -storepass YOUR_STORE_PASSWORD -keypass YOUR_KEY_PASSWORD

Enable copy and paste in a webpage from the browser console


javascript:(function(){
  allowCopyAndPaste = function(e){
  e.stopImmediatePropagation();
  return true;
  };
  document.addEventListener('copy', allowCopyAndPaste, true);
  document.addEventListener('paste', allowCopyAndPaste, true);
  document.addEventListener('onpaste', allowCopyAndPaste, true);
})();