Olá!
Pra ter a segurança que vc quer, o seu servidor onde está seu banco, não deve dar acesso externo, somente local mesmo, mas aí vc pensa q isso é loucura, como os outros vão acessar o banco de dados ou outros serviços, tipo PHP, Python e etc???
A solução é usar a arquitetura MVC, sigla do termo em inglês Model (modelo) View (visão) e Controller (Controle), já usamos aqui em alguns projetos, e aí vc vai ter que ajustar muita coisa na sua solução desktop, e somado a isso, uma solução para transacionar os dados, que seria uma API RESTful "é uma interface que dois sistemas de computador usam para trocar informações de forma segura pela internet. A maioria das aplicações de negócios precisa se comunicar com outras aplicações internas e de terceiros para executar várias tarefas." (peguei esse conceito da internet).
Aí é estudar muito essa arquitetura, pois se vc der qq acesso externo a seu servidor(es), pode ter certeza q alguém vai invadir um dia, nem que seja com ataques de força bruta, DoS e DDoS.
Foi muito trabalhoso e custoso implantar tudo isso aqui na minha empresa e confesso que nem sou eu o pai da criança, é meu sócio, que basicamente é focado somente nisso e toma 200% do tempo dele.
Mas depois vem as vantagens, pois como já temos toda a estrutura de servidores em Cloud na AWS, não temos nenhum servidor na empresa mais, já foi tudo pro lixo, bom voltando, caso eu queira acessar determinada tabela de um determinado DB, com um app desktop, é só ajustar nossa API pra receber as requisições via protocolo HTTP (Request) e tratar a resposta (Response) que vem em JSON.
Um exemplo de API de terceiros que uso com um app Desktop, é consulta a CNPJ, onde o fornecedor tem um WS ligado direto, esperando as requisições.
Link:
http://receitaws.com.br/v1/cnpj/
Código: Selecionar todos
//A título de exemplo, sem compiação e testes.
//Passa um CNPJ para uma função de consulta e retorna um Hash com os dados do CNPJ para ser processado no DBF
Function Main()
If Len( Trim(cCnpj) ) <> 14
REturn .t.
Endif
hCnpj := CNPJ_Consulta( cCnpj )
If hCnpj <> NIL
If Len(hCnpj) > 2
If nOpMenu = INCLUSAO
ccr_nome := PadR( cl_NulToString(hCnpj["nome"]), Len(ccr_nome) )
ccr_fantas := PadR( cl_NulToString(hCnpj["fantasia"]), Len(ccr_fantas) )
If Ptab( cl_NulToString(hCnpj["municipio"]), 'TBM', 2)
tbm_codigo := TBM->tbm_codigo
Endif
ccr_cep := PadR( cl_LimpaString( hCnpj["cep"]) , Len(ccr_cep) )
ccr_lograd := PadR( cl_NulToString(hCnpj["logradouro"]) , Len(ccr_lograd) )
ccr_numero := PadR( cl_NulToString(hCnpj["numero"]) , Len(ccr_numero) )
ccr_comple := PadR( cl_NulToString(hCnpj["complemento"]), Len(ccr_comple) )
ccr_bairro := PadR( cl_NulToString(hCnpj["bairro"]) , Len(ccr_bairro) )
ccr_cidade := PadR( cl_NulToString(hCnpj["municipio"]) , Len(ccr_cidade) )
ccr_uf := PadR( cl_NulToString(hCnpj["uf"]) , Len(ccr_uf) )
ccr_fones := PadR( cl_NulToString(hCnpj["telefone"]) , Len(ccr_fones) )
ccr_email := PadR( cl_NulToString(hCnpj["email"]) , Len(ccr_email) )
x := hCnpj["atividade_principal"]
cna_codigo := cl_LimpaString( x[1]["code"] )
x := aScan( aEmpresaTipo, { | a | Trim(Upper(a[2])) = cl_NulToString(hCnpj["porte"]) })
If x > 0
ccr_ETipo := aEmpresaTipo[x, 1]
Endif
Else
If LastKey() = 32 //K_F8
CCR->ccr_nome := hCnpj["nome"]
CCR->ccr_fantas := PadR( cl_NulToString(hCnpj["fantasia"]), Len(ccr_fantas) )
If Ptab( cl_NulToString(hCnpj["municipio"]), 'TBM', 2)
CCR->tbm_codigo := TBM->tbm_codigo
Endif
CCR->ccr_cep := PadR( cl_LimpaString( hCnpj["cep"]) , Len(ccr_cep) )
CCR->ccr_lograd := PadR( cl_NulToString(hCnpj["logradouro"]) , Len(ccr_lograd) )
CCR->ccr_numero := PadR( cl_NulToString(hCnpj["numero"]) , Len(ccr_numero) )
CCR->ccr_comple := PadR( cl_NulToString(hCnpj["complemento"]), Len(ccr_comple) )
CCR->ccr_bairro := PadR( cl_NulToString(hCnpj["bairro"]) , Len(ccr_bairro) )
CCR->ccr_cidade := PadR( cl_NulToString(hCnpj["municipio"]) , Len(ccr_cidade) )
CCR->ccr_uf := PadR( cl_NulToString(hCnpj["uf"]) , Len(ccr_uf) )
CCR->ccr_fones := PadR( cl_NulToString(hCnpj["telefone"]) , Len(ccr_fones) )
CCR->ccr_email := PadR( cl_NulToString(hCnpj["email"]) , Len(ccr_email) )
x := hCnpj["atividade_principal"]
CCR->cna_codigo := cl_LimpaString( x[1]["code"] )
x := aScan( aEmpresaTipo, { | a | Trim(Upper(a[2])) = cl_NulToString(hCnpj["porte"]) })
If x > 0
CCR->ccr_ETipo := aEmpresaTipo[x, 1]
Endif
msg := 'Dados do Credor/Fornecedor atualizados automaticamente de acordo com o encontrado no site da Receita Federal.'
MsgOK( msg )
Endif
Endif
Else
msg := 'não foi possível atualizar dados do Credor/Fornecedor.'
MsgAtencao( msg )
Endif
Endif
Return
////////////////////////////////////////////////////////////////////////////////
//https://pctoledo.org/forum/viewtopic.php?f=43&p=155455#p155455
//https://pctoledo.org/forum/viewtopic.php?f=4&t=23628 (com Header)
//https://pctoledo.org/forum/viewtopic.php?f=4&t=17427 (usando a hb_JSONDecode( cJSON, @hJSON ) )
//https://nfe.io/docs/desenvolvedores/rest-api/consulta-de-cnpj-v1/ (para consultas)
//https://pctoledo.org/forum/viewtopic.php?f=4&p=148594#p148594 (pode fazer FTP)
Function CNPJ_Consulta( cCnpj )
Local mSite := "http://receitaws.com.br/v1/cnpj/", oHttp, hCNPJA := Hash(), mJSONCNPJ, hJSON := Hash()
//mSite := "https://publica.cnpj.ws/cnpj/" //mais completo
If Len( Trim(cCnpj) ) <> 14 .or. cCnpj = '00000000000000' .or. !VCGC(cCnpj)
Return NIL
Endif
Begin Sequence
Try
oHttp := CreateObject("MSXML2.ServerXMLHTTP")
oHttp:Open("GET", mSite + cCNPJ, .F.)
oHttp:send()
CATCH oError
msg := 'Internet Error: 010' + CRLF
msg += 'Aviso..: Erro verificado ao fazer requisição de CNPJ no Site indicado.' + HB_Eol()
msg += 'Site...: ' + mSite + HB_Eol()
msg += 'CNPJ...: ' + cCNPJ + HB_Eol()
msg += cl_getError(oError, .t.)
msgError ( msg )
hCNPJA := NIL
Break
End
If Empty( oHttp:responseText )
hCNPJA := NIL
Break
ElseIf '"ERROR"' $ oHttp:responseText
msg := 'Request error: 015' + CRLF
msg += 'Aviso..: O retorno da consulta de CNPJ está com erros.' + HB_Eol()
msg += '- Aguarde um minuto e tente novamente.' + HB_Eol()
msg += '- Verifique se o CNPJ é válido.' + HB_Eol()
msg += 'Site...: ' + mSite + HB_Eol()
msg += 'CNPJ...: ' + cCNPJ + HB_Eol()
msg += 'Response:' + oHttp:responseText + HB_Eol()
msg += 'Status..:' + Str(oHttp:status, 3) + HB_Eol()
msg += cl_getError(, .t.)
MsgError ( msg, .t. )
hCNPJA := NIL
Break
ElseIf 'Too many' $ oHttp:responseText
msg := 'Request error: 020' + CRLF
msg += 'Aviso..: Muitas tentativas de pesquisar CNPJ no Site indicado.' + HB_Eol()
msg += '- Aguarde um minuto e tente novamente.' + HB_Eol()
msg += 'Site...: ' + mSite + HB_Eol()
msg += 'CNPJ...: ' + cCNPJ + HB_Eol()
msg += 'Response:' + oHttp:responseText + HB_Eol()
msg += 'Status..:' + Str(oHttp:status, 3) + HB_Eol()
MsgAtencao ( msg )
hCNPJA := NIL
Break
Endif
mJSONCNPJ := HB_AnsiToOem(oHttp:responseText)
hCNPJA := JSonToHash( mJSONCNPJ )
If hCNPJA = NIL
ADOStringGrava(mJSONCNPJ + CRLF)
Endif
oHttp := NIL
End Sequence
Return hCNPJA
O serviço para consulta do CNPJ está ligado 7 dias por semana, 24 horas por dia, esperando alguma requisição, não importando de onde ela venha, se de um navegador, tablet, PC, se mandou a requisição correta, ela vai ser processada e retornada uma resposta.
A API na verdade é uma aplicação desenvolvida em qualquer linguagem, VB, C#, C++, Node, Python, Java ou outra qualquer, que fica ligada no servidor esperando as requisições.
Espero ter ajudado, Abraços.