- Cliente Windows com modo silencioso e auto-start robusto - Backend Node.js + API REST - Frontend Next.js + Dashboard - Scripts PowerShell de configuração e diagnóstico - Documentação completa - Build scripts para Windows e Linux - Solução de auto-start após reinicialização Resolução do problema: Cliente não voltava ativo após reboot Solução: Registro do Windows + Task Scheduler + Modo silencioso
266 lines
6.5 KiB
Markdown
266 lines
6.5 KiB
Markdown
# Especificação Completa - Cliente Windows NoIdle
|
||
|
||
## 📋 Visão Geral
|
||
|
||
O cliente Windows deve monitorar e enviar para o servidor:
|
||
1. **Aplicativos ativos** (window_title, application_name)
|
||
2. **Histórico de navegação** (URLs do Chrome/Edge)
|
||
3. **Eventos de sessão** (logon/logoff do Windows)
|
||
|
||
---
|
||
|
||
## 🔌 Endpoints da API
|
||
|
||
### Base URL
|
||
```
|
||
https://admin.noidle.tech
|
||
```
|
||
|
||
---
|
||
|
||
## 1️⃣ Registrar Atividade (Aplicativos)
|
||
|
||
**Endpoint:** `POST /api/activity/log`
|
||
|
||
**Frequência:** A cada 5-10 segundos (quando há mudança de aplicativo/janela)
|
||
|
||
**Headers:**
|
||
```
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Body:**
|
||
```json
|
||
{
|
||
"device_id": "DEV-1762999424206-0BJR2Q",
|
||
"window_title": "Documento - Word",
|
||
"application_name": "WINWORD.EXE",
|
||
"idle_time_seconds": 0,
|
||
"urls": [
|
||
{
|
||
"url": "https://example.com/page",
|
||
"title": "Example Page",
|
||
"browser": "Chrome"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Campos Obrigatórios:**
|
||
- `device_id` (string): ID único do dispositivo
|
||
- `window_title` (string): Título da janela ativa (NÃO pode ser "System Idle" ou vazio)
|
||
- `application_name` (string): Nome do executável (ex: "chrome.exe", "WINWORD.EXE", "notepad.exe")
|
||
- `idle_time_seconds` (number): Tempo em segundos que o usuário está inativo (0 = ativo)
|
||
|
||
**Campos Opcionais:**
|
||
- `urls` (array): Array de objetos com histórico de navegação (ver seção 2)
|
||
|
||
**⚠️ IMPORTANTE:**
|
||
- **NÃO** envie `window_title = "System Idle"` ou `application_name = "[IDLE]"` quando o usuário está realmente usando um aplicativo
|
||
- Se o usuário estiver inativo por mais de 30 segundos, aí sim pode enviar `idle_time_seconds > 0`
|
||
- Capture o título real da janela e o executável real usando APIs do Windows
|
||
|
||
**Exemplo de Resposta:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Atividade registrada"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2️⃣ Histórico de Navegação (Chrome/Edge)
|
||
|
||
**Endpoint:** `POST /api/activity/log` (mesmo endpoint, campo `urls`)
|
||
|
||
**Frequência:** A cada vez que uma nova URL é visitada no navegador
|
||
|
||
**Como Capturar:**
|
||
- Use a API do Chrome/Edge para monitorar abas abertas
|
||
- Capture URLs de todas as abas ativas
|
||
- Envie no array `urls` dentro do mesmo POST de atividade
|
||
|
||
**Estrutura do Array `urls`:**
|
||
```json
|
||
{
|
||
"urls": [
|
||
{
|
||
"url": "https://www.google.com/search?q=exemplo",
|
||
"title": "exemplo - Pesquisa Google",
|
||
"browser": "Chrome"
|
||
},
|
||
{
|
||
"url": "https://github.com/user/repo",
|
||
"title": "user/repo · GitHub",
|
||
"browser": "Chrome"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Campos:**
|
||
- `url` (string): URL completa visitada
|
||
- `title` (string): Título da página/aba
|
||
- `browser` (string): "Chrome" ou "Edge"
|
||
|
||
**⚠️ IMPORTANTE:**
|
||
- Envie URLs de TODAS as abas abertas, não apenas a ativa
|
||
- Atualize a lista sempre que uma nova aba for aberta ou fechada
|
||
- Não envie URLs vazias ou inválidas
|
||
|
||
---
|
||
|
||
## 3️⃣ Eventos de Sessão (Logon/Logoff)
|
||
|
||
**Endpoint:** `POST /api/activity/session`
|
||
|
||
**Frequência:** Imediatamente quando ocorre logon ou logoff
|
||
|
||
**Headers:**
|
||
```
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Body para LOGON:**
|
||
```json
|
||
{
|
||
"device_id": "DEV-1762999424206-0BJR2Q",
|
||
"event_type": "logon",
|
||
"username": "Sergio.Dev"
|
||
}
|
||
```
|
||
|
||
**Body para LOGOFF:**
|
||
```json
|
||
{
|
||
"device_id": "DEV-1762999424206-0BJR2Q",
|
||
"event_type": "logoff",
|
||
"username": "Sergio.Dev"
|
||
}
|
||
```
|
||
|
||
**Campos Obrigatórios:**
|
||
- `device_id` (string): ID único do dispositivo
|
||
- `event_type` (string): "logon" ou "logoff"
|
||
- `username` (string): Nome do usuário do Windows
|
||
|
||
**Como Capturar:**
|
||
- Use eventos do Windows: `SessionSwitch`, `SessionLock`, `SessionUnlock`
|
||
- Monitore o evento de logon do sistema operacional
|
||
- Capture o username do usuário logado
|
||
|
||
**Exemplo de Resposta:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Evento logon registrado"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4️⃣ Heartbeat (Opcional mas Recomendado)
|
||
|
||
**Endpoint:** `POST /api/devices/heartbeat`
|
||
|
||
**Frequência:** A cada 30-60 segundos
|
||
|
||
**Body:**
|
||
```json
|
||
{
|
||
"device_id": "DEV-1762999424206-0BJR2Q"
|
||
}
|
||
```
|
||
|
||
**Nota:** O heartbeat é opcional se você estiver enviando atividades regularmente, mas ajuda a manter o dispositivo marcado como ativo.
|
||
|
||
---
|
||
|
||
## 🔧 Implementação Técnica
|
||
|
||
### Capturar Window Title e Application Name (C#/PowerShell)
|
||
|
||
```csharp
|
||
using System;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
|
||
[DllImport("user32.dll")]
|
||
static extern IntPtr GetForegroundWindow();
|
||
|
||
[DllImport("user32.dll")]
|
||
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
||
|
||
[DllImport("user32.dll")]
|
||
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
|
||
|
||
// Obter janela ativa
|
||
IntPtr hwnd = GetForegroundWindow();
|
||
StringBuilder windowTitle = new StringBuilder(256);
|
||
GetWindowText(hwnd, windowTitle, 256);
|
||
|
||
// Obter processo
|
||
uint processId;
|
||
GetWindowThreadProcessId(hwnd, out processId);
|
||
Process process = Process.GetProcessById((int)processId);
|
||
string appName = process.ProcessName + ".exe";
|
||
```
|
||
|
||
### Capturar URLs do Chrome (PowerShell)
|
||
|
||
```powershell
|
||
# Usar Chrome DevTools Protocol ou ler histórico do Chrome
|
||
# Alternativa: usar extensão do Chrome que envia dados via API local
|
||
```
|
||
|
||
### Capturar Eventos de Sessão (C#)
|
||
|
||
```csharp
|
||
using Microsoft.Win32;
|
||
|
||
SystemEvents.SessionSwitch += (sender, e) => {
|
||
if (e.Reason == SessionSwitchReason.SessionLock) {
|
||
// Logoff ou bloqueio
|
||
} else if (e.Reason == SessionSwitchReason.SessionUnlock) {
|
||
// Logon ou desbloqueio
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Checklist de Implementação
|
||
|
||
- [ ] Cliente captura `window_title` real (não "System Idle")
|
||
- [ ] Cliente captura `application_name` real (exe do processo)
|
||
- [ ] Cliente envia atividades a cada 5-10 segundos quando há mudança
|
||
- [ ] Cliente monitora e envia URLs do Chrome/Edge
|
||
- [ ] Cliente detecta eventos de logon/logoff do Windows
|
||
- [ ] Cliente envia eventos de sessão imediatamente
|
||
- [ ] Cliente trata erros de rede e faz retry
|
||
- [ ] Cliente valida dados antes de enviar
|
||
|
||
---
|
||
|
||
## 🐛 Troubleshooting
|
||
|
||
### Problema: Apenas "System Idle" aparece nas atividades
|
||
**Solução:** Verifique se o código está capturando a janela ativa corretamente. Use `GetForegroundWindow()` e não confie em valores padrão.
|
||
|
||
### Problema: URLs não aparecem
|
||
**Solução:** Verifique se o array `urls` está sendo enviado no POST `/api/activity/log` e se contém dados válidos.
|
||
|
||
### Problema: Eventos de sessão não aparecem
|
||
**Solução:** Verifique se o cliente está escutando eventos do Windows corretamente e se está fazendo POST para `/api/activity/session`.
|
||
|
||
---
|
||
|
||
## 📞 Suporte
|
||
|
||
Para problemas técnicos, verifique:
|
||
1. Logs do cliente (console/arquivo de log)
|
||
2. Logs do servidor: `pm2 logs pointcontrol-api`
|
||
3. Resposta HTTP dos endpoints (status code, mensagem de erro)
|
||
|