Pular para o conteúdo principal

Serviços Personalizados do Terminal (App Mobile)

Pilar: 02 — Funcionalidades Core / App Mobile
Status: 🟢 Implementado
Última Revisão Técnica: 2026-08-03
Módulos Conectados: gatein-server (app/api/mobile/services.py, app/models.py), gatein-app (src/screens/ServicesScreen, src/screens/ServiceWebView)


1. Visão Geral e Arquitetura do Módulo

O módulo de Serviços Personalizados permite que cada Empresa/Terminal disponibilize portais web externos, utilitários, tabelas de tarifas, agendamentos de refeição no pátio ou funcionalidades nativas para os motoristas no aplicativo mobile. As URLs externas são exibidas em um componente de WebView seguro (ServiceWebView), com injeção automática de tokens JWT de curta duração para autenticação em sistemas terceiros e controle rígido contra domínios não autorizados.

1.1 Diagrama de Sequência e Injeção de Token no WebView

sequenceDiagram
autonumber
actor M as Motorista / App Mobile
participant S as Gatein Server (FastAPI)
participant DB as PostgreSQL
participant WV as WebView (App Mobile)
participant EXT as Sistema Terceiro (Ex: Portal de Agendamento)

M->>S: GET /mobile/companies/{company_id}/services
S->>DB: Query CompanyServices join AllowedDomains (is_active == True)
S-->>M: Retorna lista de serviços com URLs e meta-dados (Sqids)
M->>M: Seleciona um serviço do tipo "webview"
M->>S: GET /mobile/services/auth-token
S->>S: Gera JWT temporário (exp: 3 minutos / 180s) com user_id
S-->>M: Retorna token de integração
M->>WV: Inicializa ServiceWebView com URL do serviço + Header Auth
WV->>EXT: GET https://terceiro.com.br/portal (Header: Authorization Bearer <temp_jwt>)
EXT->>EXT: Valida JWT do Gatein e reconhece o motorista
EXT-->>WV: Renderiza portal personalizado dentro do app

2. Estruturas de Dados e Schemas Explícitos

2.1 Modelo Relacional no Banco de Dados (company_services e allowed_domains)

Tabela company_services:

ColunaTipo SQLConstraintsDescrição / Regra
idUUIDPRIMARY KEY, Default gen_random_uuid()Identificador único interno
company_idUUIDFOREIGN KEY (companies.id), NOT NULLEmpresa proprietária do serviço
domain_idUUIDFOREIGN KEY (allowed_domains.id), NOT NULLVínculo com a whitelist de domínios
titleVARCHAR(255)NOT NULLNome do serviço exibido no card
descriptionTEXTNULLABLEDescrição das funcionalidades oferecidas
urlVARCHAR(512)NOT NULLEndereço HTTP/HTTPS do portal
icon_urlVARCHAR(512)NULLABLEURL da imagem do ícone do serviço
is_activeBOOLEANNOT NULL, Default TrueStatus de ativação
created_atTIMESTAMPTZNOT NULL, Default now()Timestamp de criação

Tabela allowed_domains:

ColunaTipo SQLConstraintsDescrição / Regra
idUUIDPRIMARY KEY, Default gen_random_uuid()Identificador único
domainVARCHAR(255)UNIQUE, NOT NULLDomínio autorizado (ex: *.terminal.com.br)
is_activeBOOLEANNOT NULL, Default TrueStatus da whitelist

2.2 Schemas de Resposta da API Mobile

Resposta de Serviços da Empresa (ServiceListResponse):

{
"success": true,
"data": [
{
"id": "w8x9k2m1",
"company_id": "p4k2n8m9",
"title": "Consulta de Fila de Pátio",
"description": "Veja a posição em tempo real do seu caminhão na triagem",
"url": "https://patios.terminal.com.br/triagem",
"icon_url": "https://cdn.gatein.app/icons/queue.png",
"is_active": true,
"created_at": "2026-08-01T10:00:00Z"
}
]
}

Resposta de Token de Integração (AuthTokenResponse):

{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 180
}
}

3. Regras de Negócio Explícitas (RN-SVC-XXX)

RN-SVC-001: Trava Dupla de Ativação por Domínio Permitido

Um serviço cadastrado na tabela company_services só é retornado no aplicativo mobile se atender a duas condições simultâneas no banco de dados:

  1. CompanyService.is_active == True
  2. AllowedDomain.is_active == True (o domínio vinculado em domain_id deve estar ativo na whitelist da plataforma).

RN-SVC-002: Token de Integração de Curta Duração (3 Minutos)

A rota GET /mobile/services/auth-token gera um JWT de integração com expiração estrita de 180 segundos (3 minutos) (exp_delta=timedelta(minutes=3)). Este token é injetado nos headers do ServiceWebView para prevenir vazamentos de credenciais permanentes em navegações de terceiros.

RN-SVC-003: Isolamento de Serviços por Empresa (Tenant Scope)

A rota GET /mobile/companies/{company_id}/services decodifica o company_id informado em formato Sqid e retorna exclusivamente os serviços vinculados àquela empresa. Não há mesclagem de serviços de múltiplos terminais em uma mesma listagem.

RN-SVC-004: Validação Rígida de Whitelist de Domínios na WebView

Antes de efetuar o carregamento da página no componente ServiceWebView, o aplicativo mobile verifica se o domínio da URL de destino pertence à lista de domínios permitidos retornada pela API. Se o motorista tentar navegar para uma URL fora da whitelist (ex: redirecionamento malicioso), a WebView interrompe o carregamento e exibe a tela de erro INVALID_DOMAIN_ACCESS.

RN-SVC-005: Fallback de Ícone do Serviço

Caso o campo icon_url seja nulo ou a imagem falhe ao carregar no aplicativo, a interface do React Native utiliza um ícone genérico padronizado em SVG (DefaultServiceIcon).


4. Detalhamento de Endpoints

4.1 GET /mobile/companies/{company_id}/services

Lista todos os serviços ativos disponibilizados por uma empresa específica.

4.2 POST /mobile/services/by-ids

Recebe um array de UUIDs/Sqids e retorna os detalhes estruturados dos serviços solicitados.

4.3 GET /mobile/services/auth-token

Gera o token JWT de integração (TTL: 180s) para o motorista autenticado.


5. Regras de Segurança e Tratamento de Erros

Código HTTPCódigo InternoCausa RaizAção no App Mobile
400 Bad RequestINVALID_COMPANY_IDHash Sqid de empresa corrompido ou inexistenteRetornar à tela anterior
403 ForbiddenDOMAIN_NOT_ALLOWEDURL do serviço pertence a domínio inativoBloquear abertura do WebView
401 UnauthorizedINTEGRATION_TOKEN_EXPIREDToken de 3 min expirou durante navegaçãoRe-solicitar token via background fetch