Update 1140

This commit is contained in:
2026-04-27 10:16:32 -03:00
parent d95a3d8086
commit f56b3f8493

317
readme.md
View File

@@ -212,6 +212,19 @@ DtUuid.get();
DtUuid.set(value); 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` ### `CakeApp.system`
```js ```js
@@ -271,7 +284,9 @@ function cleanBridgeValue(value) {
| `DtExecuteVpnStart.execute()` | sem argumentos | void | Inicia/dispara o comando nativo da VPN. | | `DtExecuteVpnStart.execute()` | sem argumentos | void | Inicia/dispara o comando nativo da VPN. |
| `DtExecuteVpnStop.execute()` | sem argumentos | void | Para a 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. | | `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. | | `DtShowLoggerDialog.execute()` | sem argumentos | void | Abre o diálogo nativo de logs. |
Possíveis estados de `DtGetVpnState` / `DtVpnStateListener`: Possíveis estados de `DtGetVpnState` / `DtVpnStateListener`:
@@ -286,6 +301,75 @@ Possíveis estados de `DtGetVpnState` / `DtVpnStateListener`:
| `NO_NETWORK` | Aguardando rede. | | `NO_NETWORK` | Aguardando rede. |
| `AUTH_FAILED` | Falha de autenticação. | | `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 ### 5.4 APIs de consulta de usuário/conta
| API | Assinatura | Retorno | Finalidade | | 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`; 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 ### 5.7 APIs de config visual/app
| API | Assinatura | Retorno | Finalidade | | 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 ## 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: 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 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. - Campos de usuário quando os metadados contêm `username`, `usuario`, `login` ou tokens parecidos com usuário.
@@ -595,31 +744,21 @@ HTML recomendado:
</div> </div>
``` ```
### 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 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 ```json
{ "mode": "V2RAY" } { "mode": "V2RAY" }
```
Isto funciona:
```json
{ "mode": "XRAY" } { "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": "VMESS" }
{ "mode": "VLESS" } { "mode": "VLESS" }
{ "mode": "V2RAY - VMESS" } { "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 ```json
{ {
@@ -719,10 +858,23 @@ Exemplo:
[ [
{ "14:03:55": "Connecting" }, { "14:03:55": "Connecting" },
{ "14:03:56": "Authenticating" }, { "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: Helper de renderização:
```js ```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.DtUuid = { get: () => '', set: (v) => console.log('uuid', v) };
window.DtExecuteVpnStart = { execute: () => console.log('start') }; window.DtExecuteVpnStart = { execute: () => console.log('start') };
window.DtExecuteVpnStop = { execute: () => console.log('stop') }; 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()); 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 ### Erro: salvar autenticação somente no conectar
@@ -1117,6 +1308,26 @@ const OnlineBridge = {
this.set('DtUsername', username || ''); this.set('DtUsername', username || '');
this.set('DtPassword', password || ''); this.set('DtPassword', password || '');
this.set('DtUuid', uuid || ''); 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. - Não mostra `Locked` para o usuário.
- Consegue fazer parse seguro de JSON vazio ou inválido. - Consegue fazer parse seguro de JSON vazio ou inválido.
- Escuta `DtVpnStateListener`. - 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. - Chama `DtSetConfig.execute(id)` antes de iniciar um perfil selecionado.
- Salva usuário/senha/UUID antes de conectar. - Salva usuário/senha/UUID antes de conectar.
- Trata `auth.username`, `auth.password` e `auth.v2ray_uuid`. - 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 depende de `DtGetPingResult` retornar ping real.
- Não coloca código de produção abaixo do marcador de mock. - 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. - 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);
}
```