Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.
Comentário: Revertida da versão 40

O AppLinker é uma ferramenta que permite a integração entre os Aplicativos móveis  da linha Backoffice e o Protheus local do usuário,  e também permite a visualização de  LOGS para fins de suporte.


Observação:  O AppLinker deve ser utilizado somente para fins de demonstrações e suporte.


Para utilizar, acesse https://applinker.engpro.totvs.com.br/


Para efetuar o login é necessário ter um usuário cadastrado e aprovado.

Caso não possua um usuário basta cadastrar clicando no botão NOVO REGISTRO

Image Added


Nota
titleIMPORTANTE

O modelo de autenticação foi alterado a partir do dia 06/11/2020, sendo necessário fazer um novo cadastro para utilização do APPLINKER.


Preencher os campos solicitados e clicar no botão CADASTRAR


Image Added

O cadastro será realizado e ficará PENDENTE aprovação, o processo de aprovação pode levar até 24 horas, em caso de dúvidas entrar em contato com e-mail [email protected]

Image Added

Após receber o e-mail do cadastro aprovado o seu usuário está pronto para uso.

Image Added

Para a geração de um ALIAS temporário seguir os passos abaixo:


1 - REST PROTHEUS

Preencher as informações de configuração do REST de seu Protheus local.

Porta - Porta que o REST está configurado

URL - path onde o REST irá subir


Image Added


Os campos acima devem ser preenchidos de acordo com o seu appserver.ini, 

exemplo:


Image Added


Para mais informações sobre o como configurar o REST.
acesse: https://tdn.totvs.com/x/fEn6Hg

2 - APP Linker

Após clicar em conectar, se a conexão for bem sucedida você será redirecionado a seguinte pagina.

Nesta pagina será necessário escolher o APP que serão realizado os testes.
De acordo com o APP selecionado, exibirá campos personalizados, como por exemplo, empresa e filial, que são campos
obrigatórios.

Image Added


Nota
titleIMPORTANTE

Por meio desta ferramenta (APPLINKER) o uso do aplicativo Meu Protheus NÃO É COMPATÍVEL com a funcionalidade de NOTIFICAÇÕES. Para tal, deve ser criado um ALIAS através do Mingle (https://tdn.totvs.com/x/b_ITEQ).



Na escolha do APP MINHA PRESTAÇÃO DE CONTAS, exibirá os campos disponíveis para serem preenchidos, no caso,
empresa e filial.


Image Added


Possuindo tudo configurado, aperte o botão "Conectar".

3- START

Será apresentando a seguinte tela, onde exibirá o ALIAS, que será:  APP + seu email.



Nota
titleIMPORTANTE

Ao fechar o navegador é encerrado a conexão, sendo necessário estar com o navegador aberto enquanto estiver utilizando o ALIAS no aplicativo. 


Image Added


Após as etapas, na tela do aplicativo, você utiliza o usuário e a senha do Protheus configurado no REST
e o ALIAS gerado no applinker.


Image Added



Demais documentações


Documentação Aplicativos Móveis

https://tdn.totvs.com/x/Wm9KE


Documentação de aplicativos WEB (Somente para TOTVERS)

https://tdn.totvs.com/x/RXXjIg


Solicitar Alias

TOTVS Mingle https://mingle.totvs.com.br/landpage/


Image Added

...

borderColor#BAD0F0
bgColor#BADBFF
borderStylesolid

...

Aviso

Nesta documentação não será exibido qualquer dado sensivel sobre a aplicação. Qualquer acesso deverá ser solicitado para a equipe Squad Mobile ([email protected]).

...

effectDuration0.5
idcabecalho
effectTypeslide

...

defaulttrue
effectDuration0.5
idapplinker
labelAppLinker
titleApplinker
effectTypeslide

...

effectDuration0.5
idvertentes

...

idintroducao
labelIntrodução
titleIntrodução

...

width80%

Propósito 

O AppLinker é uma ferramenta desenvolvida com o objetivo de permitir a criação de Alias para bases locais PROTHEUS

...

Determinados aplicativos necessitam realizar integração com o intermediador (gateway), também conhecido como Mingle, para conexão com os servers. 

Isso se faz necessário para que a URL do servidor não fique exposta ou disponível para todos. Trazendo assim estabilidade e segurança para o ambiente. 

Os aplicativos móveis a seguir necessitam de Alias

  • Legal Process
  • Legal Task
  • Meu Posto de Trabalho
  • Meu Protheus
  • Minha Prestação de Contas
  • Meus Ativos Fixos 
  • Meus Contratos
  • Minha Gestão de Postos 

...

effectDuration0.5
idfuncionalidades-interfaces
labelFuncionalidades e Interfaces
titleFuncionalidades e Interfaces

...

effectDuration0.5
idaplicacao
Card
effectDuration0.5
idacesso
labelAcesso
titleAcesso

Primeiro Login

Para acessar o AppLinker, é necessário ter um usuário liberado. 

Informe os dados de "Usuário" e "Senha" e clique em "Acessar sistema".

Painel
titleTenha de Login - AppLinker

Image Removed

Card
effectDuration0.5
idnovo-usuario
labelNovo Usuário
titleNovo Usuário

Solicitando Nova Credencial

Para criar um novo usuário clique em "Novo Registro" em seguida informe os dados necessários de "E-mail" e "Senha". 

Aviso

Para o cadastro é necessário que o usuário seja um TOTVER. Outras organizações não são permitidas. 

Painel
titleCadastro - AppLinker

Image Removed

Card
effectDuration0.5
idrecuperar-senha
labelRecuperar Senha
titleRecuperar Senha

Resgatar Palavra Chave

Para recuperar as credenciais de acesso, informe o seu "E-mail", confirme no seu e-mail a troca de senha e altere sua senha. 

Painel
titleRecuperação de Senha - AppLinker

Image Removed

Card
effectDuration0.5
idconexao-server
labelConexão com o Server
titleConexão com o Server

Rest Protheus

Preencher as informações de configuração do REST de seu Protheus local.

Porta - Porta que o REST está configurado

URL - path onde o REST irá subir

Painel
titleRest Protheus - AppLinker

Image Removed

Dica

Os campos acima devem ser preenchidos de acordo com o seu appserver.ini, exemplo:

Painel

Image Removed

Card
effectDuration0.5
idescolher-aplicativo
labelEscolher aplicativo
titleEscolher aplicativo

Aplicativo

Após clicar em conectar, se a conexão for bem sucedida você será redirecionado a seguinte página. Nesta pagina será necessário escolher o APP que serão realizados os testes.
De acordo com o APP selecionado, exibirá campos personalizados, como por exemplo, empresa e filial, que são campos obrigatórios.

Painel
titleAplicativo - AppLinker

Image Removed

Nota

Para o aplicativo "Meu Protheus NÃO É COMPATÍVEL" com a funcionalidade de notificações. Para tal, deve ser criado um ALIAS através do Mingle, ou pelo Assistente de Configuração Mobile.

...

effectDuration0.5
idalias-gerado
labelAlias Gerado
titlealias-gerado

Start

Após as etapas, na tela do aplicativo, você utiliza o usuário e a senha do Protheus configurado no REST
e o ALIAS gerado no Applinker.

...

titleAlias - AppLinker

Image Removed

Painel
titleAplicativo

Image Removed

...

effectDuration0.5
idconfiguracao-instrucoes-tecnicas
labelConfiguração e Instruções Técnicas
titleConfiguração e Instruções Técnicas

...

startHiddenfalse
effectDuration0.5
iddesenvolvimento-producao

...

idprojeto
labelProjeto
titleProjeto

...

effectDuration0.5
iddocumentos-tecnicos
Card
idcaso-uso
labelCaso de Uso
titleCaso de Uso

Funcionalidades do Usuário

A seguir você irá visualizar os requisitos de usuário. 

Painel
titleCaso de Uso

Image Removed

...

idfluxo-usuario
labelFluxo do Usuário
titleFluxo do Usuário

Diagrama de Uso

A seguir você irá visualizar um passo a passo de como funciona a aplicação e todas as entidades presentes. 

Painel
titleDiagrama de Uso

Image Removed

Card
effectDuration0.5
idmodelo-conceitual-logico
labelModelo Conceitual e Lógico
titleModelo Conceitual e Lógico

Diagramas

A modelagem de dados do AppLinker é bem "simples".

É composta apenas por uma tabela de "Usuários", que guarda o "e-mail", "senha", "permissão" e "data do registro". 

Painel
titleModelo Conceitual

Image Removed

Painel
titleModelo Lógico

Image Removed

...

effectDuration0.5
idprodução
labelProdução
titleProdução

...

effectDuration0.5
idambiente-producao

...

effectDuration0.5
idbanco-dados
labelBanco de Dados
titleBanco de Dados

...

effectDuration0.5
iddatabase
Card
effectDuration0.5
idcriando-ambiente-sql
labelCriando o Ambiente SQL
titleCriando o Ambiente SQL

Persistência de Dados

Query para a criação da tabela de usuários: 

Bloco de código
languagesql
themeConfluence
firstline1
titleCriando Tabela de Usuários
CREATE TABLE public.tb_users
(
    id integer NOT NULL DEFAULT nextval('tb_users_seq'::regclass),
    email character varying(50) COLLATE pg_catalog."default" NOT NULL,
    password character varying(50) COLLATE pg_catalog."default",
    enable boolean,
    register text COLLATE pg_catalog."default" DEFAULT now()
)

...

effectDuration0.5
idacesso
labelAcesso
titleAcesso

Conexão em Produção  

Para acessar a página de administração do banco de dados acesse o PGADMIN.

Acesse usando o usuário administrador do banco. 

Para encontrar a tabela vá em Servers → Applinker → Databases → AppLinker → Schemas → Public → Tables → tb_users 

...

titleAcessando as tabelas

Image Removed

...

effectDuration0.5
idusuarios
labelUsuários
titleUsuários

Permissões de Usuários

Após aberta a tabela você poderá clicar com o botão direito do mouse, clicar sobre Query Tool,  e em seguida será aberta uma tela para que possa ser criado querys SQL na tabela.  

...

Image Removed

Aviso

Caso o serviço de verificação de e-mails esteja instável ou fora do ar, será necessário alterar a permissão por uma query.

O campo "enable" é que diz se o usuário têm ou não permissão para entrar no Applinker

Para alterar a permissão do usuário execute: 

UPDATE tb_users SET enable = true WHERE email = 'email_do_usuario@totvs.com.br';

...

effectDuration0.5
idadministracao-usuarios
labelAdministração de Usuários
titleAdministração de Usuários

Serviço de E-mails

O AppLinker possui um mecanismo para criação de novos usuários e recuperação de senha.

O processo para criação de novos usuários se resume em: 

...

O processo para recuperação de senha: 

  1. Usuário informa o e-mail para recuperação de senha pelo AppLinker
  2. O AppLinker envia um e-mail com o link para recuperação de senha. 
  3. Usuário informa a nova senha e envia as alterações.

O server do AppLinker é responsável por todo esse provisionamento do serviço de e-mails. 

As informações do cadastro do serviço de e-mails com autenticação de dois fatores está no Google Cloud

Aviso

Por motivos de segurança não será posto nenhum gif do processo para acessar as informações presentes no Google Cloud.

Para acessar as informações selecione o time "Google Play Android Developer". 

Na seção "IDs do Cliente OAuth 2.0" clique sobre "App Linker Emails".

Neste painel você terá acesso as informações presentes no server do AppLinker, no arquivo smtp.js.

Card
idprovisionamento-aplicação
labelProvisionamento da Aplicação
titleProvisionamento da Aplicação
Deck of Cards
effectDuration0.5
idAcessos
Card
effectDuration0.5
idpainel-acesso
labelPainel de Acesso
titlePainel de Acesso

Gestão com Rancher

Rancher é uma plataforma para gestão de aplicações Docker.

Por meio do Rancher disponibilizamos e gerenciamos as aplicações Backend e Frontend do AppLinker

Para acessar os containers Docker: 

  1. Acesse o painel de administração do Rancher.
  2. Informe as credenciais de acesso. (As credenciais são seu usuário e senha de rede). 
  3. Após logado você será direcionado para página de "Clusters". Acesse o projeto clicando no cluster "engpro-eks" e em seguida "mobile".

Painel

Image Removed

Card
effectDuration0.5
iddeploy-restart-aplicacoes
labelDeploy e Restart das Aplicações
titleDeploy e Restart das Aplicações

Administrando Containers

Em alguns momentos, as aplicações ficam fora do ar ou apresentam instabilidade. 

Neste caso será necessário reiniciar as aplicações. 

Para reiniciar as aplicações selecione applinker-client e o AppLinker-server  e em seguida clique em "Redeploy". 

Painel
Image Removed
Card
effectDuration0.5
iddesenvolvimento
labelDesenvolvimento
titleDesenvolvimento
Deck of Cards
startHiddenfalse
effectDuration0.5
idconfiguracao-tecnica
Card
effectDuration0.5
idinstalacao
labelInstalação
titleInstalação

Clone dos Projetos 

O AppLinker possui dois repositórios.

Um contém o projeto Frontend e o outro o Backend

Aviso

É necessário solicitar uma licença para ter acesso ao Azure.

Caso o usuário no Azure seja um StackHolder ou nem tenha login, o mesmo deve preencher o formulário de solicitação pelo link: 
https://fluig.totvs.com/portal/p/10097/pageworkflowview?processID=solicitacaoLicencasAzureDevops

Segue os links para os repositórios:

Expandir
titleFrontend

https://totvstfs.visualstudio.com/ServicesMobile/_git/AppLinker%20-%20Client

Expandir
titleBackend

https://totvstfs.visualstudio.com/ServicesMobile/_git/AppLinker%20-%20Server

Card
effectDuration0.5
idambiente-desenvolvimento
labelAmbiente de Desenvolvimento
titleAmbiente de Desenvolvimento

Configurar Máquina

Para subir o client e o server do AppLinker em uma máquina para fins de desenvolvimento, é necessário instalar algumas dependencias.

Segue uma lista de tecnologias que devem estar presentes. 

Para subir o client

Para subir o server:

Instale o Banco de Dados Postgree para testes locais.

As branchs para iniciar o desenvolvimento de qualquer feature devem ser feitas a partir da "develop" (tanto client como server)

Card
effectDuration0.5
idaponte-local
labelAponte para Local
titleAponte para Local
Deck of Cards
effectDuration0.5
idarquivos
Card
effectDuration0.5
idfrontend
labelFrontend
titleFrontend

Alterando para LocalHost

Aviso

Todas as requisições são feitas para ambiente de produção. Altere os arquivos para que sejam direcionadas para localhost. 

Note a seguir que o client faz todas as suas requisições usando um provider HttpService.

Neste HttpService usamos uma constante que possui a URL para onde estamos apontando, para localhost ou produção. 

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerClient/app/src/app/providers/http.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment';
const APP_API = environment.api;


@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor(private http: HttpClient) { }

  get(endpoint) {
    return this.http.get(`${APP_API}/${endpoint}`);
  }

  post(endpoint, body = {}) {
    return this.http.post(`${APP_API}/${endpoint}`, body);
  }

}

No arquivo environment.ts altere o valor do objeto de:  

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerClient/app/src/environments/environment.ts
export const environment = {
production: true,
api: 'https://applinkerserver.applinker.engpro.totvs.com.br'
};

para: 

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerClient/app/src/environments/environment.ts
export const environment = {
production: false,
api: 'http://localhost:3000'
};
Card
effectDuration0.5
idbackend
labelBackend
titleBackend

Serviço de E-mails

Aviso

Todas as requisições são feitas para ambiente de produção. Altere os arquivos para que sejam direcionadas para localhost. 

Para que as funcionalidades "Ativação de Usuários", "Recuperação de Senha" funcionem em ambiente local, altere os dados da configuração do banco e do ambiente de produção.

No arquivo config_db.ts altere o valor do objeto de:  

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerServer/server/modules/database/config_db.ts
export class ConfigDB {
  private conn;

  public async connectDB() {

    const { Pool, Client } = require('pg')
    
    const connectionString = "postgres://applinker:dado_sensivel@postgres:5432/applinker"
                            
    const pool = new Pool({
      connectionString: connectionString,
    })

    pool
    .connect()
    .then( ()=> console.info('conectado com sucesso'))
    .catch( () => console.info('falha ao conectar com o banco de dados.'))

    return await pool
    }
}

para:

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerServer/server/modules/database/config_db.ts
export class ConfigDB {
  private conn;

  public async connectDB() {

    const { Pool, Client } = require('pg')
    
    const connectionString = "postgres://postgres:123456@localhost:5432/applinker";
                            
    const pool = new Pool({
      connectionString: connectionString,
    })

    pool
    .connect()
    .then( ()=> console.info('conectado com sucesso'))
    .catch( () => console.info('falha ao conectar com o banco de dados.'))

    return await pool
    }
}

No arquivo controller.ts altere o código de:  

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerServer/server/modules/auth/controller.ts
import { ConfigDB } from './../database/config_db';
import { Request, Response } from 'express';
import * as ldap from 'ldapjs';
import * as jwt from 'jsonwebtoken';
import * as nodemailer from 'nodemailer';
import { google } from 'googleapis';

const SMTP_CONFIG = require('./../../config/env/smtp')

const config = require('../../config/env/')();

// declaracao do OAuth2
const OAuth2 = google.auth.OAuth2;

export class AuthController {

  protected client;
  protected adSuffix = "dc=local";
  constructor() {

    // this.client = ldap.createClient({
    //     url: config.activeDirectory.url
    // });

    // this.client.bind(config.activeDirectory.user, config.activeDirectory.password, err => {
    //     if (err) {
    //         throw new Error('user LDAP não autenticado!');
    //     }
    // });

  }

  public auth(req: Request, res: Response, next: any) {

    this.authWithoutAD(req, res, next);

  }
  // public authWithAD(req: Request, res: Response, next: any) {
  //     const searchOptions = {
  //         scope: "sub",
  //         filter: `(&(objectClass=person)(sAMAccountName=${req.body.user})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))`
  //     };

  //     this.client.search(this.adSuffix,searchOptions,(err, response) => {
  //         console.log('logando...');
  //         if (err) {
  //             console.log(`erro ao efetuar as busca ${err}`);
  //         }
  //         response.on('searchEntry', entry => {
  //             console.log('buscando login...');
  //             this.client.bind(entry.object.dn, req.body.password, (err) => {
  //                 if (err) {
  //                     res.status(400); 
  //                     res.send({error: "Unauthorized"});
  //                 } else {
  //                     res.send({
  //                         user: entry.object.sAMAccountName,
  //                         userEmail: entry.object.mail,
  //                         token: jwt.sign({user: entry.object.sAMAccountName}, config.masterKey)
  //                     });
  //                 }
  //                 res.end();

  //             });

  //         });

  //     });

  //     req.setTimeout(20000);
  // }


  public async register(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();

      let result = await conn.query("select * from tb_users where email = $1 ", [req.body.user]);

      if (result.rows.length > 0) {
        return res.status(400).send({ error: "O email ja esta sendo utilizado." });
      }

      result = await conn.query("insert into tb_users (email, password,enable) values ($1, md5($2),false)", [req.body.user, req.body.password]);

      if (!result.err) {
        res.send({
          userEmail: req.body.user
        });
        await this.sentEmail(req.body.user)
      } else {
        res.status(400).send({ error: "Não foi possivel cadastrar" });
      }

      conn.end();
    } catch (err) {
      return res.status(500).send({ error: err.name });
    }
  }

  public async authWithoutAD(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();

      let result = await conn.query("select email from tb_users where email = $1 and password = md5($2) and enable = true", [req.body.user, req.body.password]);

      if (result.rowCount > 0) {
        res.send({
          user: req.body.user.split("@")[0],
          userEmail: req.body.user,
          token: jwt.sign({ user: req.body.user }, config.masterKey)
        });
      } else {
        result = await conn.query("select email from tb_users where email = $1 and password = md5($2) and enable = false", [req.body.user, req.body.password]);
        if (result.rowCount > 0) {
          res.status(401).send({ error: "Usuário aguardando aprovação " });
        } else {
          res.status(403).send({ error: "Senha ou email incorretos " });
        }

      }

    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }

  // Acrescentando OAuth2 Authentication
  public async sentEmail(email: string): Promise<void> {
    const emailToken = jwt.sign({ email }, config.masterKey);

    const oauth2Client = new OAuth2(SMTP_CONFIG.client_id, SMTP_CONFIG.secret_key, SMTP_CONFIG.redirect_uri);

    oauth2Client.setCredentials({
      refresh_token: SMTP_CONFIG.refresh_token
    })

    const accessToken = await oauth2Client.getAccessToken().then((token) => {
      return token.token;
    })

    const transporter = await nodemailer.createTransport({
      host: SMTP_CONFIG.host,
      port: SMTP_CONFIG.port,
      secure: true,
      auth: {
        type: 'OAuth2',
        user: SMTP_CONFIG.user,
        clientId: SMTP_CONFIG.client_id,
        clientSecret: SMTP_CONFIG.secret_key,
        refreshToken: SMTP_CONFIG.refresh_token,
        accessToken: accessToken
      }
    })


    await transporter.sendMail({
      subject: 'Verificação de acesso ao AppLinker',
      from: 'Protheus Mobile' + `<${SMTP_CONFIG.user}>`,
      to: `${email}`,
      html: `<div>
                <div
                  style="background:#4DBFBF;background-color:#4DBFBF;margin:0px auto;max-width:600px;"
                >
                  <table
                    align="center"
                    border="0"
                    cellpadding="0"
                    cellspacing="0"
                    role="presentation"
                    style="background:#4DBFBF;background-color:#ffffff;width:100%;"
                  >
                    <tbody>
                      <tr>
                        <td
                          style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"
                        >
                          <div
                            class="dys-column-per-100 outlook-group-fix"
                            style="direction:ltr;display:inline-block;font-size:13px;text-align:left;vertical-align:top;width:100%;"
                          >
                            <table
                              border="0"
                              cellpadding="0"
                              cellspacing="0"
                              role="presentation"
                              style="vertical-align:top;"
                              width="100%"
                            >
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                  <div
                                    style='color:#0c9abe;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:36px;line-height:1;text-align:center;'
                                  >
                                    Boas Vindas
                                  </div>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                  <div
                                    style='color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;line-height:20px;text-align:center;'
                                  >
                                    <br />
                                    Para ativar sua conta no AppLinker clique no botão abaixo:
                                    <br />
                                    <br />
                                  </div>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                  vertical-align="middle"
                                >
                                  <table
                                    border="0"
                                    cellpadding="0"
                                    cellspacing="0"
                                    role="presentation"
                                    style="border-collapse:separate;line-height:100%;width:200px;"
                                  >
                                    <tr>
                                      <td
                                        align="center"
                                        bgcolor="#178F8F"
                                        role="presentation"
                                        style='background-color:#0c9abe; border:"solid 1px red";border-radius:4px;cursor:auto;padding:10px 25px;'
                                        valign="middle"
                                      >
                                        <a
                                          href="https://applinkerserver.applinker.engpro.totvs.com.br/auth/validate/${emailToken}"
                                          style='background-color:#0c9abe; color:white;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;font-weight:bold;line-height:30px;margin:0;text-decoration:none;text-transform:none;'
                                          target="_blank"
                                        >
                                          Ativar
                                        </a>
                                      </td>
                                    </tr>
                                  </table>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                    <div
                                    style=' color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:12px;margin-top:10;text-decoration:none;text-transform:none;'
                                    >
                                        Para mais detalhes a respeito do uso da AppLinker <a href="https://tdn.totvs.com/x/KHbQHw">clique aqui!<a/>
                                    </div>
                                </td>
                              </tr>
                            </table>
                          </div>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
              >`
    }).catch(error => {
      console.log(error)
    })
  }

  public async sentEmailToReset(req: Request, res: Response, next: any): Promise<void> {
    try {
    
      const email = req.body.user;
      const emailToken = jwt.sign({ email }, config.masterKey);
      const oauth2Client = new OAuth2(SMTP_CONFIG.client_id, SMTP_CONFIG.secret_key, SMTP_CONFIG.redirect_uri);

      oauth2Client.setCredentials({
        refresh_token: SMTP_CONFIG.refresh_token
      })

      const accessToken = await oauth2Client.getAccessToken().then((token) => {
        return token.token;
      })

    
      const transporter = await nodemailer.createTransport({
        host: SMTP_CONFIG.host,
        port: SMTP_CONFIG.port,
        secure: true,
        auth: {
          type: 'OAuth2',
          user: SMTP_CONFIG.user,
          clientId: SMTP_CONFIG.client_id,
          clientSecret: SMTP_CONFIG.secret_key,
          refreshToken: SMTP_CONFIG.refresh_token,
          accessToken: accessToken
        }
      })

      await transporter.sendMail({
        subject: 'Verificação de acesso ao AppLinker',
        from: 'Protheus Mobile' + `<${SMTP_CONFIG.user}>`,
        to: `${email}`,
        html: `<div>
              <div
                style="background:#4DBFBF;background-color:#4DBFBF;margin:0px auto;max-width:600px;"
              >
                <table
                  align="center"
                  border="0"
                  cellpadding="0"
                  cellspacing="0"
                  role="presentation"
                  style="background:#4DBFBF;background-color:#ffffff;width:100%;"
                >
                  <tbody>
                    <tr>
                      <td
                        style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"
                      >
                        <div
                          class="dys-column-per-100 outlook-group-fix"
                          style="direction:ltr;display:inline-block;font-size:13px;text-align:left;vertical-align:top;width:100%;"
                        >
                          <table
                            border="0"
                            cellpadding="0"
                            cellspacing="0"
                            role="presentation"
                            style="vertical-align:top;"
                            width="100%"
                          >
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                <div
                                  style='color:#0c9abe;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:36px;line-height:1;text-align:center;'
                                >
                                  Boas Vindas
                                </div>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                <div
                                  style='color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;line-height:20px;text-align:center;'
                                >
                                  <br />
                                  Para alterar a senha cadastrada no AppLinker clique no botão abaixo:
                                  <br />
                                  <br />
                                </div>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                vertical-align="middle"
                              >
                                <table
                                  border="0"
                                  cellpadding="0"
                                  cellspacing="0"
                                  role="presentation"
                                  style="border-collapse:separate;line-height:100%;width:200px;"
                                >
                                  <tr>
                                    <td
                                      align="center"
                                      bgcolor="#178F8F"
                                      role="presentation"
                                      style='background-color:#0c9abe; border:"solid 1px red";border-radius:4px;cursor:auto;padding:10px 25px;'
                                      valign="middle"
                                    >
                                      <a
                                        href="https://applinker.engpro.totvs.com.br/reset-password?token=${emailToken}"
                                        style='background-color:#0c9abe; color:white;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;font-weight:bold;line-height:30px;margin:0;text-decoration:none;text-transform:none;'
                                        target="_blank"
                                      >
                                        Alterar Senha
                                      </a>
                                    </td>
                                  </tr>
                                </table>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                  <div
                                  style=' color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:12px;margin-top:10;text-decoration:none;text-transform:none;'
                                  >
                                      Para mais detalhes a respeito do uso da AppLinker <a href="https://tdn.totvs.com/x/KHbQHw">clique aqui!<a/>
                                  </div>
                              </td>
                            </tr>
                          </table>
                        </div>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>`
      }).then(() => {
        res.status(200).send({ message: 'Email enviado com sucesso.' });
      }).catch(error => {
        res.status(500).send({ message: 'Não foi possivel enviar o email.' })
      })
    } catch (err) {
      console.log('aqui')
      res.status(500).send({ error: err.name });
    }

  }



  public async validate(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();

      const token = req.params.token;
      if (token) {
        jwt.verify(token, config.masterKey, async (err, decodedToken) => {
          if (err) {
            return res.status(400).json({ error: 'Incorrect token' });
          }
          const { email } = decodedToken;
          let result = await conn.query("update tb_users set enable = true where email = $1 and enable = false", [email]);
          return res.redirect('https://applinker.engpro.totvs.com.br/login?isActive=true')
        })

      } else {
        res.status(500).send({ error: 'Invalid token' });
      }
    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }

  public async resetPassword(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();
      const token = req.body.token;
      if (token) {
        jwt.verify(token, config.masterKey, async (err, decodedToken) => {
          if (err) {
            return res.status(400).json({ error: 'Incorrect token' });
          }
          const { email } = decodedToken;
          let result = await conn.query("update tb_users set password = md5($2) where email = $1", [email, req.body.password]);
          if (result.rowCount > 0) {
            res.status(200).send({ message: "Senha alterada com sucesso" });
          } else {
            res.status(403).send({ error: "Não foi possivel alterar a senha." });
          }
        })
      } else {
        res.status(500).send({ error: 'Invalid token' });
      }
    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }

}

para: 

Bloco de código
languagejs
themeMidnight
firstline1
titleAppLinkerServer/server/modules/auth/controller.ts
import { ConfigDB } from './../database/config_db';
import { Request, Response } from 'express';
import * as jwt from 'jsonwebtoken';
import * as nodemailer from 'nodemailer';
import { google } from 'googleapis';

const SMTP_CONFIG = require('./../../config/env/smtp')

const config = require('../../config/env/')();

// declaracao do OAuth2
const OAuth2 = google.auth.OAuth2;

export class AuthController {

  protected client;
  protected adSuffix = "dc=local";
  constructor() {}

  public auth(req: Request, res: Response, next: any) {

    this.authWithoutAD(req, res, next);

  }
  
  public async register(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();
      let result = await conn.query("select * from tb_users where email = $1 ", [req.body.user]);
      if (result.rows.length > 0) {
        return res.status(400).send({ error: "O email ja esta sendo utilizado." });
      }
      await conn.query("insert into tb_users (email, password,enable) values ($1, md5($2),false)", [req.body.user, req.body.password]).then(res => {
        result = res
      }).catch(error => {
        console.log(error);
      });

      if (!result.err) {
        res.send({
          userEmail: req.body.user
        });
        await this.sentEmail(req.body.user)
      } else {
        res.status(400).send({ error: "Não foi possivel cadastrar" });
      }

      conn.end();
    } catch (err) {
      return res.status(500).send({ error: err.name });
    }
  }

  public async authWithoutAD(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();

      let result = await conn.query("select email from tb_users where email = $1 and password = md5($2) and enable = true", [req.body.user, req.body.password]);

      if (result.rowCount > 0) {
        res.send({
          user: req.body.user.split("@")[0],
          userEmail: req.body.user,
          token: jwt.sign({ user: req.body.user }, config.masterKey)
        });
      } else {
        result = await conn.query("select email from tb_users where email = $1 and password = md5($2) and enable = false", [req.body.user, req.body.password]);
        if (result.rowCount > 0) {
          res.status(401).send({ error: "Usuário aguardando aprovação " });
        } else {
          res.status(403).send({ error: "Senha ou email incorretos " });
        }

      }

    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }

  // Acrescentando OAuth2 Authentication
  public async sentEmail(email: string): Promise<void> {
    const emailToken = jwt.sign({ email }, config.masterKey);

    const oauth2Client = new OAuth2(SMTP_CONFIG.client_id, SMTP_CONFIG.secret_key, SMTP_CONFIG.redirect_uri);

    oauth2Client.setCredentials({
      refresh_token: SMTP_CONFIG.refresh_token
    })

    const accessToken = await oauth2Client.getAccessToken().then((token) => {
      return token.token;
    })

    const transporter = await nodemailer.createTransport({
      host: SMTP_CONFIG.host,
      port: SMTP_CONFIG.port,
      secure: true,
      auth: {
        type: 'OAuth2',
        user: SMTP_CONFIG.user,
        clientId: SMTP_CONFIG.client_id,
        clientSecret: SMTP_CONFIG.secret_key,
        refreshToken: SMTP_CONFIG.refresh_token,
        accessToken: accessToken
      }
    })


    await transporter.sendMail({
      subject: 'Verificação de acesso ao AppLinker',
      from: 'Protheus Mobile' + `<${SMTP_CONFIG.user}>`,
      to: `${email}`,
      html: `<div>
                <div
                  style="background:#4DBFBF;background-color:#4DBFBF;margin:0px auto;max-width:600px;"
                >
                  <table
                    align="center"
                    border="0"
                    cellpadding="0"
                    cellspacing="0"
                    role="presentation"
                    style="background:#4DBFBF;background-color:#ffffff;width:100%;"
                  >
                    <tbody>
                      <tr>
                        <td
                          style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"
                        >
                          <div
                            class="dys-column-per-100 outlook-group-fix"
                            style="direction:ltr;display:inline-block;font-size:13px;text-align:left;vertical-align:top;width:100%;"
                          >
                            <table
                              border="0"
                              cellpadding="0"
                              cellspacing="0"
                              role="presentation"
                              style="vertical-align:top;"
                              width="100%"
                            >
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                  <div
                                    style='color:#0c9abe;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:36px;line-height:1;text-align:center;'
                                  >
                                    Boas Vindas
                                  </div>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                  <div
                                    style='color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;line-height:20px;text-align:center;'
                                  >
                                    <br />
                                    Para ativar sua conta no AppLinker clique no botão abaixo:
                                    <br />
                                    <br />
                                  </div>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                  vertical-align="middle"
                                >
                                  <table
                                    border="0"
                                    cellpadding="0"
                                    cellspacing="0"
                                    role="presentation"
                                    style="border-collapse:separate;line-height:100%;width:200px;"
                                  >
                                    <tr>
                                      <td
                                        align="center"
                                        bgcolor="#178F8F"
                                        role="presentation"
                                        style='background-color:#0c9abe; border:"solid 1px red";border-radius:4px;cursor:auto;padding:10px 25px;'
                                        valign="middle"
                                      >
                                        <a
                                        href="http://localhost:3000/auth/validate/${emailToken}"
                                        style='background-color:#0c9abe; color:white;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;font-weight:bold;line-height:30px;margin:0;text-decoration:none;text-transform:none;'
                                          target="_blank"
                                        >
                                          Ativar
                                        </a>
                                      </td>
                                    </tr>
                                  </table>
                                </td>
                              </tr>
                              <tr>
                                <td
                                  align="center"
                                  style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                >
                                    <div
                                    style=' color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:12px;margin-top:10;text-decoration:none;text-transform:none;'
                                    >
                                        Para mais detalhes a respeito do uso da AppLinker <a href="https://tdn.totvs.com/x/KHbQHw">clique aqui!<a/>
                                    </div>
                                </td>
                              </tr>
                            </table>
                          </div>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
              >`
    }).catch(error => {
      console.log(error)
    })
  }

  public async sentEmailToReset(req: Request, res: Response, next: any): Promise<void> {
    try {
    
      const email = req.body.user;
      const emailToken = jwt.sign({ email }, config.masterKey);
      const oauth2Client = new OAuth2(SMTP_CONFIG.client_id, SMTP_CONFIG.secret_key, SMTP_CONFIG.redirect_uri);

      oauth2Client.setCredentials({
        refresh_token: SMTP_CONFIG.refresh_token
      })

      const accessToken = await oauth2Client.getAccessToken().then((token) => {
        return token.token;
      })

    
      const transporter = await nodemailer.createTransport({
        host: SMTP_CONFIG.host,
        port: SMTP_CONFIG.port,
        secure: true,
        auth: {
          type: 'OAuth2',
          user: SMTP_CONFIG.user,
          clientId: SMTP_CONFIG.client_id,
          clientSecret: SMTP_CONFIG.secret_key,
          refreshToken: SMTP_CONFIG.refresh_token,
          accessToken: accessToken
        }
      })

      await transporter.sendMail({
        subject: 'Verificação de acesso ao AppLinker',
        from: 'Protheus Mobile' + `<${SMTP_CONFIG.user}>`,
        to: `${email}`,
        html: `<div>
              <div
                style="background:#4DBFBF;background-color:#4DBFBF;margin:0px auto;max-width:600px;"
              >
                <table
                  align="center"
                  border="0"
                  cellpadding="0"
                  cellspacing="0"
                  role="presentation"
                  style="background:#4DBFBF;background-color:#ffffff;width:100%;"
                >
                  <tbody>
                    <tr>
                      <td
                        style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"
                      >
                        <div
                          class="dys-column-per-100 outlook-group-fix"
                          style="direction:ltr;display:inline-block;font-size:13px;text-align:left;vertical-align:top;width:100%;"
                        >
                          <table
                            border="0"
                            cellpadding="0"
                            cellspacing="0"
                            role="presentation"
                            style="vertical-align:top;"
                            width="100%"
                          >
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                <div
                                  style='color:#0c9abe;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:36px;line-height:1;text-align:center;'
                                >
                                  Boas Vindas
                                </div>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                <div
                                  style='color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;line-height:20px;text-align:center;'
                                >
                                  <br />
                                  Para alterar a senha cadastrada no AppLinker clique no botão abaixo:
                                  <br />
                                  <br />
                                </div>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                                vertical-align="middle"
                              >
                                <table
                                  border="0"
                                  cellpadding="0"
                                  cellspacing="0"
                                  role="presentation"
                                  style="border-collapse:separate;line-height:100%;width:200px;"
                                >
                                  <tr>
                                    <td
                                      align="center"
                                      bgcolor="#178F8F"
                                      role="presentation"
                                      style='background-color:#0c9abe; border:"solid 1px red";border-radius:4px;cursor:auto;padding:10px 25px;'
                                      valign="middle"
                                    >
                                      <a
                                      href="http://localhost:3000/reset-password?token=${emailToken}"
                                      style='background-color:#0c9abe; color:white;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:16px;font-weight:bold;line-height:30px;margin:0;text-decoration:none;text-transform:none;'
                                        target="_blank"
                                      >
                                        Alterar Senha
                                      </a>
                                    </td>
                                  </tr>
                                </table>
                              </td>
                            </tr>
                            <tr>
                              <td
                                align="center"
                                style="font-size:0px;padding:10px 25px;word-break:break-word;"
                              >
                                  <div
                                  style=' color:#586a6e;font-family:"Droid Sans", "Helvetica Neue", Arial, sans-serif;font-size:12px;margin-top:10;text-decoration:none;text-transform:none;'
                                  >
                                      Para mais detalhes a respeito do uso da AppLinker <a href="https://tdn.totvs.com/x/KHbQHw">clique aqui!<a/>
                                  </div>
                              </td>
                            </tr>
                          </table>
                        </div>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>`
      }).then(() => {
        res.status(200).send({ message: 'Email enviado com sucesso.' });
      }).catch(error => {
        res.status(500).send({ message: 'Não foi possivel enviar o email.' })
      })
    } catch (err) {
      res.status(500).send({ error: err.name });
    }

  }



  public async validate(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();

      const token = req.params.token;
      if (token) {
        jwt.verify(token, config.masterKey, async (err, decodedToken) => {
          if (err) {
            return res.status(400).json({ error: 'Incorrect token' });
          }
          const { email } = decodedToken;
          let result = await conn.query("update tb_users set enable = true where email = $1 and enable = false", [email]);
          return res.redirect('http://localhost:4200/login?isActive=true')
        })

      } else {
        res.status(500).send({ error: 'Invalid token' });
      }
    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }

  public async resetPassword(req: Request, res: Response, next: any) {
    const db = new ConfigDB();
    try {
      const conn = await db.connectDB();
      const token = req.body.token;
      if (token) {
        jwt.verify(token, config.masterKey, async (err, decodedToken) => {
          if (err) {
            return res.status(400).json({ error: 'Incorrect token' });
          }
          const { email } = decodedToken;
          let result = await conn.query("update tb_users set password = md5($2) where email = $1", [email, req.body.password]);
          if (result.rowCount > 0) {
            res.status(200).send({ message: "Senha alterada com sucesso" });
          } else {
            res.status(403).send({ error: "Não foi possivel alterar a senha." });
          }
        })
      } else {
        res.status(500).send({ error: 'Invalid token' });
      }
    } catch (err) {
      res.status(500).send({ error: err.name });
    }
  }
}
Card
effectDuration0.5
idbanco-dados
labelBanco de Dados
titleBanco de Dados
Deck of Cards
effectDuration0.5
idbd
Card
effectDuration0.5
idcrie-server
labelCrie um Server
titleCrie um Server

Criando o DataBase

Para criar um Banco de Dados clique com o botão direito em "Databases" e depois em "Create". 

De o nome de "applinker" e clique em "Save". 

Painel
titleCriação do Server

Image Removed

Card
idtabela
labelTabela
titleTabela

Criando a Tabela

Para criar uma tabela no Banco de Dados clique com o botão direito em "Tables" e depois em "Query Tool". 

Com o editor de querys aberto execute o script. 

Bloco de código
languagesql
themeConfluence
firstline1
titleAmbiente Local
CREATE TABLE public.tb_users
(
    id serial NOT NULL,
    email character varying(50) COLLATE pg_catalog."default" NOT NULL,
    password character varying(50) COLLATE pg_catalog."default",
    enable boolean,
    register text COLLATE pg_catalog."default" DEFAULT now()
)

Segue a baixo uma representação: 

Painel
titleCriando a Tabela

Image Removed

...

defaulttrue
effectDuration0.5
idsuporte
labelSuporte
titleSuporte
effectTypeslide

...

Perguntas frequentes (FAQs)

Expandir
titleAcesso a ferramenta

https://AppLinker.engpro.totvs.com.br/login

Expandir
titleSolicitação do Alias pelo portal do Mingle

https://mingle.totvs.com.br/landpage/

Expandir
titleDocumentação dos Aplicativos Móveis

https://tdn.totvs.com/x/Wm9KE

Expandir
titleConfigurar REST

https://tdn.totvs.com/x/fEn6Hg

Expandir
titleAplicativos na Web

https://tdn.totvs.com/x/RXXjIg

Expandir
titleCriar Alias pelo Assistente de Configuração Mobile

https://acm.engpro.totvs.com.br/index.html

HTML
<!-- esconder o menu --> <style> div.theme-default .ia-splitter #main { margin-left: 0px; } .ia-fixed-sidebar, .ia-splitter-left { display: none; } #main { padding-left: 10px; padding-right: 10px; overflow-x: hidden; } .aui-header-primary .aui-nav, .aui-page-panel { margin-left: 0px !important; } .aui-header-primary .aui-nav { margin-left: 0px !important; } </style>
HTML
<style>

.aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a, .aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a:link, .aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a:visited, .aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a:focus, .aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a:hover, .aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a:active {
color: white;
}

.aui-tabs.horizontal-tabs>.tabs-menu>.menu-item.active-tab a::after {
background: white; !important}

...