From f56b3f8493a7579b4895bd98ae3586928ea699f4 Mon Sep 17 00:00:00 2001 From: penguinehis Date: Mon, 27 Apr 2026 10:16:32 -0300 Subject: [PATCH] Update 1140 --- readme.md | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 296 insertions(+), 21 deletions(-) diff --git a/readme.md b/readme.md index 67cdb09..19af705 100644 --- a/readme.md +++ b/readme.md @@ -212,6 +212,19 @@ DtUuid.get(); DtUuid.set(value); ``` +### `CakeApp.errors` + +```js +CakeApp.errors.getLast(); // retorna string JSON com o último erro de conexão +``` + +Aliases equivalentes: + +```js +DtGetLastConnectionError.execute(); +DtGetConnectionError.execute(); +``` + ### `CakeApp.system` ```js @@ -271,7 +284,9 @@ function cleanBridgeValue(value) { | `DtExecuteVpnStart.execute()` | sem argumentos | void | Inicia/dispara o comando nativo da VPN. | | `DtExecuteVpnStop.execute()` | sem argumentos | void | Para a VPN. | | `DtGetNetworkDownloadBytes.execute()` | sem argumentos | string numérica | Retorna bytes baixados atuais da rede/app para verificação de tráfego. | -| `DtGetLogs.execute()` | sem argumentos | string JSON | Retorna logs recentes de status/banner como array de objetos com timestamp. | +| `DtGetLogs.execute()` | sem argumentos | string JSON | Retorna logs recentes de status/banner/proxy como array de objetos com timestamp. | +| `DtGetLastConnectionError.execute()` | sem argumentos | string JSON | Retorna o último erro classificado de conexão, autenticação ou proxy. Retorna `{}` se não houver erro. | +| `DtGetConnectionError.execute()` | sem argumentos | string JSON | Alias de compatibilidade de `DtGetLastConnectionError.execute()`. | | `DtShowLoggerDialog.execute()` | sem argumentos | void | Abre o diálogo nativo de logs. | Possíveis estados de `DtGetVpnState` / `DtVpnStateListener`: @@ -286,6 +301,75 @@ Possíveis estados de `DtGetVpnState` / `DtVpnStateListener`: | `NO_NETWORK` | Aguardando rede. | | `AUTH_FAILED` | Falha de autenticação. | +#### 5.3.1 Erros de conexão/autenticação/proxy + +O patch expõe um bridge específico para o tema entender por que a conexão falhou, sem precisar depender apenas de texto solto no botão ou no status. + +```js +function getLastConnectionError() { + try { + return JSON.parse(DtGetLastConnectionError.execute() || '{}'); + } catch (e) { + return {}; + } +} +``` + +Formato retornado: + +```json +{ + "state": "AUTH_FAILED", + "code": "LOGIN_INVALID", + "message": "Authentication failed", + "time": 1777290000000 +} +``` + +Campos: + +| Campo | Tipo | Significado | +|---|---|---| +| `state` | string | Estado nativo/mapeado no momento do erro. | +| `code` | string | Código normalizado para o tema tratar com UI própria. | +| `message` | string | Mensagem original limpa vinda do status/log/banner/proxy. | +| `time` | number | Timestamp em milissegundos. | + +Códigos conhecidos nesta versão: + +| Código | Quando aparece | Uso recomendado no tema | +|---|---|---| +| `LOGIN_EXPIRED` | Mensagens contendo acesso expirado/vencido. | Mostrar aviso de renovação. | +| `LOGIN_INVALID` | Falha de usuário/senha, HTTP 401 ou texto de login inválido. | Mostrar usuário/senha inválidos. | +| `LOGIN_LIMIT` | Mensagens de limite/conexões excedidas. | Mostrar limite de conexões atingido. | +| `PROXY_FORBIDDEN` | HTTP 403 ou `Forbidden`. | Mostrar proxy/bughost bloqueado. | +| `PROXY_BAD_REQUEST` | HTTP 400 ou `Bad Request`. | Mostrar payload/proxy inválido. | +| `PROXY_RATE_LIMITED` | HTTP 429 ou `Too Many Requests`. | Mostrar muitas tentativas/limite temporário. | +| `PROXY_SERVER_ERROR` | HTTP 500/502, `Bad Gateway` ou erro interno. | Mostrar falha no proxy/servidor remoto. | +| `AUTH_FAILED` | Falha de autenticação sem classificação mais específica. | Mostrar falha genérica de autenticação. | + +Exemplo de uso direto: + +```js +function renderLastError() { + const error = getLastConnectionError(); + if (!error.code) return; + + const messages = { + LOGIN_EXPIRED: 'Seu login expirou. Renove seu acesso.', + LOGIN_INVALID: 'Usuário ou senha inválidos.', + LOGIN_LIMIT: 'Limite de conexões atingido.', + PROXY_FORBIDDEN: 'Proxy/bughost bloqueou a conexão.', + PROXY_BAD_REQUEST: 'Payload ou proxy retornou Bad Request.', + PROXY_RATE_LIMITED: 'Muitas tentativas. Tente novamente mais tarde.', + PROXY_SERVER_ERROR: 'Proxy/servidor remoto retornou erro.', + AUTH_FAILED: 'Falha na autenticação.' + }; + + showError(messages[error.code] || error.message || 'Falha na conexão.'); +} +``` + ### 5.4 APIs de consulta de usuário/conta | API | Assinatura | Retorno | Finalidade | @@ -356,6 +440,27 @@ A porta padrão de proxy usada pelo tema existente é `6821`, mas o bridge em si const proxyAddress = `${DtGetLocalIP.execute()}:6821`; ``` +Nesta versão do patch, `DtStartHotSpotService.execute()` não inicia mais o socket diretamente pelo `ProxyServer`. Ele dispara o `ProxyService` com `ACTION_START`, para que o Android mostre uma notificação foreground persistente do proxy, por exemplo: + +```txt +Proxy ONLINE - 192.168.x.x:6821 +``` + +Ao chamar `DtStopHotSpotService.execute()`, o app envia `ACTION_STOP` para o mesmo serviço e remove a notificação. + +Em Android 13+ o app precisa da permissão `POST_NOTIFICATIONS`. O `OnlineConfigWebActivity` tenta pedir essa permissão ao abrir e também antes de iniciar o proxy. Se o usuário negar, o proxy não deve ser considerado iniciado pelo tema. Sempre confirme o status depois do comando: + +```js +async function startLocalProxyAndRefresh() { + DtStartHotSpotService.execute(); + + setTimeout(() => { + const status = DtGetStatusHotSpotService.execute(); + renderProxyStatus(status); + }, 500); +} +``` + ### 5.7 APIs de config visual/app | API | Assinatura | Retorno | Finalidade | @@ -454,6 +559,50 @@ window.dtCheckUserModelListener = function(modelJson) { }; ``` +### `window.dtLogsUpdatedListener = function(logsJson) {}` + +Chamado quando o app adiciona um log visível para o tema. Também existe o alias com inicial maiúscula `window.DtLogsUpdatedListener`. + +```js +window.dtLogsUpdatedListener = function(logsJson) { + let rows = []; + try { rows = JSON.parse(logsJson || '[]'); } catch (e) {} + + const text = rows.map(row => { + const time = Object.keys(row)[0] || ''; + return `${time} ${row[time] || ''}`; + }).join('\n'); + + renderLogs(text); +}; +``` + +### `window.dtConnectionErrorListener = function(error) {}` + +Chamado quando o app classifica um erro de conexão/autenticação/proxy. Também existem estes aliases: + +```js +window.DtConnectionErrorListener = function(error) {}; +window.dtAuthErrorListener = function(error) {}; +window.DtAuthErrorListener = function(error) {}; +``` + +O argumento já chega como objeto JavaScript, não como string JSON. + +```js +window.dtConnectionErrorListener = function(error) { + if (!error || !error.code) return; + + if (error.code === 'LOGIN_EXPIRED') { + showError('Seu login expirou. Renove seu acesso.'); + } else if (error.code === 'PROXY_FORBIDDEN') { + showError('Proxy/bughost bloqueou a conexão.'); + } else { + showError(error.message || 'Falha na conexão.'); + } +}; +``` + --- ## 7. JSON de config retornado para o HTML @@ -568,7 +717,7 @@ Ele varre todos os elementos `input`, `textarea` e `select`, e classifica eles p Ele detecta: -- Campos de UUID quando os metadados contêm `v2ray`, `uuid` ou `xray`. +- Campos de UUID quando os metadados contêm `v2ray`, `uuid`, `xray`, `vmess` ou `vless`. - Campos de senha quando os metadados contêm `password`, `senha` ou `pass`. - Campos de usuário quando os metadados contêm `username`, `usuario`, `login` ou tokens parecidos com usuário. @@ -595,31 +744,21 @@ HTML recomendado: ``` -### Limitação importante desta versão +### Detecção V2Ray/Xray/VMess/VLess nesta versão -Nesta versão do `VOIDPRO.zip`, a função injetada de visibilidade considera V2Ray/Xray somente quando `mode` começa com `v2ray` ou `xray`. +No patch atual, a função injetada de visibilidade já considera `v2ray`, `xray`, `vmess` e `vless` no `mode` e também nos metadados do input. -Isto funciona: +Estes formatos são detectados como perfis baseados em UUID: ```json { "mode": "V2RAY" } -``` - -Isto funciona: - -```json { "mode": "XRAY" } -``` - -Estes podem não ser detectados pela função injetada, a não ser que o tema tenha sua própria lógica de visibilidade ou você aplique patch no Java: - -```json { "mode": "VMESS" } { "mode": "VLESS" } { "mode": "V2RAY - VMESS" } ``` -Para builds atuais, prefira este formato de perfil no catálogo para configs baseadas em UUID: +Mesmo assim, o formato mais limpo para configs baseadas em UUID continua sendo: ```json { @@ -719,10 +858,23 @@ Exemplo: [ { "14:03:55": "Connecting" }, { "14:03:56": "Authenticating" }, - { "14:03:57": "Server Message: welcome" } + { "14:03:57": "Server Message: welcome" }, + { "14:03:58": "Proxy Status [HTTP_PROXY]: HTTP/1.1 101 Switching Protocols" }, + { "14:04:01": "Proxy Status [SSH_DIRECT]: HTTP/1.1 302 Found" } ] ``` +O patch faz o bridge mostrar também respostas HTTP/proxy relevantes para o tema, incluindo: + +- `Proxy Status [HTTP_PROXY]: HTTP/1.1 101 ...` +- `Proxy Status [HTTP_PROXY]: HTTP/1.1 302 ...` +- `Proxy Status [SSH_DIRECT]: HTTP/1.1 101 ...` +- `Proxy Status [SSH_DIRECT]: HTTP/1.1 302 ...` +- `HTTP/1.1 400`, `401`, `403`, `404`, `429`, `500`, `502` +- mensagens como `Unauthorized`, `Forbidden`, `Bad Request`, `Too Many Requests` e `Bad Gateway` + +A diferença importante é o `SSH_DIRECT`: quando o campo de servidor SSH é usado como proxy/bughost e o servidor real está dentro do payload, o app agora faz uma leitura curta e segura da primeira resposta. Se vier HTTP, ele mostra o status para o HTML. Se vier banner SSH, ele preserva os bytes e não quebra o handshake. + Helper de renderização: ```js @@ -737,7 +889,23 @@ function readLogs() { } ``` -O buffer de logs nativo exposto ao HTML é limitado a entradas recentes de status e mensagens de banner do servidor. Ele não é o logcat/debug completo. +Listener ao vivo: + +```js +window.dtLogsUpdatedListener = function(logsJson) { + let rows = []; + try { rows = JSON.parse(logsJson || '[]'); } catch (e) {} + + const latest = rows.length ? Object.values(rows[rows.length - 1])[0] : ''; + if (latest.includes('Proxy Status [SSH_DIRECT]')) { + showProxyStatus(latest); + } + + renderLogs(readLogs()); +}; +``` + +O buffer de logs nativo exposto ao HTML é limitado a entradas recentes de status, mensagens de banner do servidor e mensagens úteis de proxy. Ele não é o logcat/debug completo. --- @@ -963,6 +1131,12 @@ if (!window.DtGetVpnState) { window.DtUuid = { get: () => '', set: (v) => console.log('uuid', v) }; window.DtExecuteVpnStart = { execute: () => console.log('start') }; window.DtExecuteVpnStop = { execute: () => console.log('stop') }; + window.DtGetLogs = { execute: () => JSON.stringify([]) }; + window.DtGetLastConnectionError = { execute: () => '{}' }; + window.DtGetConnectionError = window.DtGetLastConnectionError; + window.DtGetStatusHotSpotService = { execute: () => 'STOPPED' }; + window.DtStartHotSpotService = { execute: () => console.log('proxy start') }; + window.DtStopHotSpotService = { execute: () => console.log('proxy stop') }; } ``` @@ -999,9 +1173,26 @@ Bom: usernameInput.value = cleanBridgeValue(DtUsername.get()); ``` -### Erro: assumir que todos os modos V2Ray são detectados automaticamente pelo código de visibilidade injetado +### Erro: não tratar erros classificados do bridge -Use `mode: "V2RAY"` no catálogo/settings para este build, ou adicione um helper de visibilidade no tema que também verifique `vmess` e `vless`. +Ruim: + +```js +window.DtVpnStateListener = function(state) { + if (state === 'AUTH_FAILED') alert('Falha'); +}; +``` + +Bom: + +```js +window.dtConnectionErrorListener = function(error) { + if (!error || !error.code) return; + showError(error.message || error.code); +}; +``` + +Use `DtGetLastConnectionError.execute()` para recuperar o último erro quando a tela for recriada ou quando o tema carregar depois de uma falha. ### Erro: salvar autenticação somente no conectar @@ -1117,6 +1308,26 @@ const OnlineBridge = { this.set('DtUsername', username || ''); this.set('DtPassword', password || ''); this.set('DtUuid', uuid || ''); + }, + + readLogs() { + return this.jsonFromExec('DtGetLogs', []); + }, + + getLastError() { + return this.jsonFromExec('DtGetLastConnectionError', {}); + }, + + getProxyStatus() { + return this.exec('DtGetStatusHotSpotService', 'STOPPED'); + }, + + startProxy() { + this.command('DtStartHotSpotService'); + }, + + stopProxy() { + this.command('DtStopHotSpotService'); } }; ``` @@ -1131,10 +1342,74 @@ Antes de publicar um tema, verifique: - Não mostra `Locked` para o usuário. - Consegue fazer parse seguro de JSON vazio ou inválido. - Escuta `DtVpnStateListener`. +- Escuta `dtLogsUpdatedListener` se o tema mostra logs/proxy-status em tempo real. +- Escuta `dtConnectionErrorListener` ou lê `DtGetLastConnectionError.execute()` para mostrar erro correto. - Chama `DtSetConfig.execute(id)` antes de iniciar um perfil selecionado. - Salva usuário/senha/UUID antes de conectar. - Trata `auth.username`, `auth.password` e `auth.v2ray_uuid`. -- O campo UUID aparece para configs `mode: "V2RAY"` / `mode: "XRAY"`. +- O campo UUID aparece para configs `mode: "V2RAY"`, `"XRAY"`, `"VMESS"` e `"VLESS"`. +- Se usar proxy local, confirma `DtGetStatusHotSpotService.execute()` depois de chamar start/stop. +- Se usar proxy local em Android 13+, considera que a notificação pode depender da permissão `POST_NOTIFICATIONS`. - Não depende de `DtGetPingResult` retornar ping real. - Não coloca código de produção abaixo do marcador de mock. - Usa APIs nativas de altura das barras ou CSS safe areas para a navegação inferior não ficar cortada. + +--- + +## 21. Resumo dos bridges novos do patch V2/V3/V4 + +Esta versão do patch adiciona/atualiza estes pontos para temas HTML OnlineConfig: + +| Item | Bridge/callback | O que muda para o tema | +|---|---|---| +| Último erro de conexão | `DtGetLastConnectionError.execute()` | Tema consegue saber se foi login expirado, login inválido, limite, proxy bloqueado, erro 400/403/429/500 etc. | +| Alias de erro | `DtGetConnectionError.execute()` | Mesmo retorno de `DtGetLastConnectionError.execute()`. | +| Helper limpo | `CakeApp.errors.getLast()` | Wrapper `CakeApp` para buscar o último erro. | +| Callback de erro | `dtConnectionErrorListener(error)` | Tema recebe o erro ao vivo como objeto JS. | +| Callback de auth/compatibilidade | `dtAuthErrorListener(error)` | Alias compatível para temas que tratam erro de autenticação separado. | +| Callback de logs | `dtLogsUpdatedListener(logsJson)` | Tema recebe logs recentes sempre que uma mensagem visível é adicionada. | +| Status de proxy HTTP | `DtGetLogs.execute()` | Logs agora incluem `Proxy Status [HTTP_PROXY]` e `Proxy Status [SSH_DIRECT]`. | +| SSH_DIRECT com payload/proxy | logs + erro classificado | Mesmo em SSH direto, o app tenta detectar resposta HTTP inicial do proxy sem quebrar banner SSH. | +| Proxy local foreground | `DtStartHotSpotService.execute()` | Inicia `ProxyService` com notificação foreground em vez de ligar apenas o socket interno. | +| Permissão de notificação | abertura do `OnlineConfigWebActivity` e start do proxy | Android 13+ pede `POST_NOTIFICATIONS`; se negar, o tema deve confirmar o status do proxy. | +| Detecção UUID | `CakeApp.applyAuthVisibility()` | Agora considera `v2ray`, `xray`, `vmess` e `vless`. | + +Exemplo completo para tema tratar logs e erros: + +```js +function parseBridgeJson(raw, fallback) { + try { return JSON.parse(raw || ''); } catch (e) { return fallback; } +} + +window.dtLogsUpdatedListener = function(logsJson) { + const logs = parseBridgeJson(logsJson, []); + const text = logs.map(row => { + const time = Object.keys(row)[0] || ''; + return `${time} ${row[time] || ''}`; + }).join('\n'); + + renderLogs(text); +}; + +window.dtConnectionErrorListener = function(error) { + if (!error || !error.code) return; + + const messageByCode = { + LOGIN_EXPIRED: 'Seu login expirou. Renove seu acesso.', + LOGIN_INVALID: 'Usuário ou senha inválidos.', + LOGIN_LIMIT: 'Limite de conexões atingido.', + PROXY_FORBIDDEN: 'Proxy/bughost bloqueou a conexão.', + PROXY_BAD_REQUEST: 'Payload ou proxy inválido.', + PROXY_RATE_LIMITED: 'Muitas tentativas no proxy.', + PROXY_SERVER_ERROR: 'Erro no proxy/servidor remoto.', + AUTH_FAILED: 'Falha de autenticação.' + }; + + renderError(messageByCode[error.code] || error.message || 'Falha na conexão.'); +}; + +function restoreLastErrorOnLoad() { + const error = parseBridgeJson(DtGetLastConnectionError.execute(), {}); + if (error.code) window.dtConnectionErrorListener(error); +} +```