O SysSentinel Server é a aplicação central responsável por receber, validar e persistir as informações enviadas pelos agentes monitorados. Ele expõe endpoints REST protegidos por JWT para autenticação, gerenciamento de usuários e consulta de métricas, atuando como núcleo de controle e agregação do sistema.
-
-
1) Endpoints do Agente (Cliente → Servidor)
POST /api/systems/sysinfoPOST /api/systems/sysinfovolatileGET /api/systems/updateAuth
-
2) Endpoints do Frontend (Frontend → Servidor)
GET /api/metrics/systemsGET /api/metrics/systemVolatileInfoPOST /api/metrics/systemRegister
-
3) Endpoints de Usuário (Frontend → Servidor)
POST /api/user/loginPOST /api/user/register
-
- recebe cadastro/atualização de informações do agente (cliente) via JSON
- expõe endpoints para o frontend consultar lista de sistemas e métricas voláteis (incluindo processos)
- gerencia usuários (register/login) com senha em BCrypt
- autentica via JWT HS256 (Resource Server)
Pasta principal: SysSentinel-Server/
Principais pacotes:
-
com.bolota.syssentinel.ControllersHostToClientController— endpoints usados pelo cliente/agenteHostToMetricsFrontendController— endpoints usados pelo frontendHostToUserFrontendController— login/register e remoção de vínculo user↔systemWebController— serveGET /retornando templateindex
-
com.bolota.syssentinel.EntitiesSystemEntities— entidades JPA persistidas (SystemEntityPersistent / SystemVolatileEntityPersistent)DTOs— modelos usados no tráfego JSONUserEntities— entidade JPA de usuário
-
com.bolota.syssentinel.SecuritySecurityConfig,JwtConfig,SystemSecurity
-
com.bolota.syssentinel.Resource- repositories JPA (SystemEntityResources, SystemVolatileEntityResources, UserEntityResources)
-
src/main/resourcesstatic/etemplates/(build do front servido pelo Spring)
- Java compatível com Spring Boot 3.5.x
- Maven
O servidor lê duas chaves de:
src/main/java/com/bolota/syssentinel/Security/authKey.config
Formato (exatamente assim):
AuthKey=<string>
RegisterKey=<string>Existe um método
SystemSecurity.registerAuthKey()que pede essas chaves viastdine grava no arquivo, e ele está ligado automaticamente ao fluxo de inicialização .
AuthKeyé usada para assinar/verificar JWT (HS256) — viaJwtConfig+SystemSecurity.getAuthKey().RegisterKeyé usada como “segredo” para permitir registro/provisionamento do agente (via headerRegisterTokenem endpoints específicos) — viaSystemSecurity.getRegisterKey().
Warning
A RegisterKey definida no Servidor deve ser idêntica à que for definida no Cliente, caso contrário, o cliente não conseguirá fazer uma requisição para receber um JWT próprio. .
Dentro de SysSentinel-Server/:
mvn spring-boot:runConfig atual (em application.yaml):
-
Porta:
8080 -
Bind:
0.0.0.0 -
Banco:
H2em memória (jdbc:h2:mem:test) -
Console H2:
- habilitado em
/h2-console web-allow-others: true
- habilitado em
O servidor funciona como OAuth2 Resource Server JWT (Spring Security), então endpoints marcados como authenticated() exigem:
- Header padrão:
Authorization: Bearer <token>
Além disso, alguns endpoints usam headers próprios (JwtToken, RegisterToken, login, etc.), porque o código explicitamente lê esses valores.
Emitido por HostToUserFrontendController.issueLoginToken():
subject: login do usuário- claim
roles:["USER"] - expiração:
now + 60*60*3(3 horas)
Emitido por HostToClientController.issueAgentToken():
subject: UUID do sistema- claim
roles:["AGENT"] - expiração:
now + 60segundos (1 minuto)
Importante: o endpoint
/api/systems/sysinfonão usa Authorization Bearer. Ele recebe o token pelo headerJwtTokene valida manualmente comjwtDecoder.decode().
Entidades persistidas:
-
SystemEntityPersistent(@Entity)@Id=UUID(String)- campos:
UUID, name, os, host, cpu, gpu (List<String>), memRamMax
-
SystemVolatileEntityPersistent(@Entity)-
@Id=UUID(String) -
campos stringificados em JSON:
basicComputerInfointernetCurrentUsageinternetAdapterssystemProcessEntities(@Lob)
-
-
UserEntity(@Entity)idautoincrementloginúnicopasswordHash(BCrypt)systemsInPossession(ArrayList)
Serialização JSON usada internamente:
SysSentinelService.genJSON(Object)usa JacksonObjectMapper.writeValueAsString.
Base: /api/systems
Controller: HostToClientController.SysEntityHandler
Headers exigidos pelo código:
JwtToken(string; pode ser"null")RegisterToken(string; pode ser"null")
Body: JSON do tipo SystemEntityPersistent:
Campos (conforme SystemEntityPersistent.java):
{
"UUID": "string",
"name": "string",
"os": "string",
"host": "string",
"cpu": "string",
"gpu": ["string", "..."],
"memRamMax": 0.0
}Fluxos exatos implementados:
- Primeiro registro (gerar UUID)
-
Condição:
seNew.UUID == "null"JwtToken == "null"RegisterToken == getRegisterKey()
-
Ação:
- servidor gera UUID aleatório (15 chars)
- emite token AGENT (exp 60s)
- salva
SystemEntityPersistent(seNew)já com UUID setado
-
Resposta:
200com JSON:
{ "UUID": "<novoUUID>", "token": "<jwtAgente>" }- Re-registro com UUID existente (provisionar token)
-
Condição:
seNew.UUID != "null"JwtToken == "null"RegisterToken == getRegisterKey()
-
Se UUID existe no banco:
- salva
SystemEntityPersistent(seNew) - responde
200com{ "UUID": "<uuid>", "token": "<jwtAgente>" }
- salva
-
Se UUID não existe:
- responde
401com{ "UUID": null, "token": null }
- responde
- UUID null mas mandando JwtToken
-
Condição:
seNew.UUID == "null"JwtToken != "null"RegisterToken == "null"
-
Resposta:
404com{ "UUID": null, "token": null }
- Atualização normal (com JwtToken)
-
O código tenta decodificar
JwtTokene exige:seNew.UUID == jwt.subject
-
Se bater:
- salva
SystemEntityPersistent(seNew) - retorna
200com{ "UUID": "<uuid>", "token": "<mesmo JwtToken>" }
- salva
-
Se não bater / token inválido:
- retorna
401com{ "UUID": null, "token": null }
- retorna
Controller: HostToClientController.SysVolatileHandler
Auth:
- precisa
Authorization: Bearer <jwtAgente>(porque o endpoint estáauthenticated()noSecurityConfig) - usa
@AuthenticationPrincipal Jwt jwt
Body: JSON do tipo SystemVolatileEntityDTO:
Campos (conforme SystemVolatileEntityDTO.java):
{
"UUID": "string",
"basicComputerInfo": { "k": "v" },
"internetCurrentUsage": { "k": 0.0 },
"internetAdapters": { "k": "v" },
"systemProcessEntities": [
{ "name": "string", "pid": 0, "residentMem": 0.0, "virtualMem": 0.0, "cpuLoad": 0.0 }
]
}Regras do handler (como está no código):
-
Se
jwt == null⇒401 -
Se
sveNew.UUID == null⇒403 -
Se
sveNew.UUID != jwt.subject⇒403 -
Se
SystemEntitycom esse UUID não existe ⇒404 -
Persistência:
-
se já existe volatile com UUID:
deleteByUUID(uuid)e depoissave(new SystemVolatileEntityPersistent(sveNew))
-
senão:
save(new SystemVolatileEntityPersistent(sveNew))
-
-
Resposta final:
200
Observação importante: aqui o projeto faz “update” do volatile via delete+save quando já existe.
Controller: HostToClientController.updateAuth
Headers:
JwtTokenRegisterTokensysUUID
Comportamento:
-
Se
JwtToken == "null"eRegisterToken == getRegisterKey():- retorna
200com{ "UUID": "<sysUUID>", "token": "<novo jwtAgente>" }
- retorna
-
Caso contrário:
- retorna
200com{ "UUID": "<sysUUID>", "token": null }
- retorna
Base: /api/metrics
Controller: HostToMetricsFrontendController.sendSystems
Auth:
Authorization: Bearer <jwtUser>
Header:
login: <string>(obrigatório no código)
Query (paginação):
- usa
Pageablecom defaultsize = 10 - aceita parâmetros padrão do Spring Data (
page,size, etc.)
Regras:
- se
jwt == nulloulogin == null⇒401 - se
jwt.subject != login⇒401 - se usuário não existe (
!uer.existsByLogin(login)) ⇒401
Resposta:
200comPage<SystemEntityDTO>composto pelos UUIDs que o usuário tem emsystemsInPossession.
Implementação: pega os UUIDs do usuário, faz
ser.getByUUID(uuid)para cada um, monta uma lista e aplica paginação comtoPage(list, pageable).
Controller: HostToMetricsFrontendController.sendSystemVolatileInfo
Auth:
Authorization: Bearer <jwtUser>
Header:
login: <string>
Query params:
uuid=<uuid do sistema>sort=<campo,ordem>(default:cpuLoad,desc)
Campos de sort implementados no switch:
namepidresidentMemvirtualMemcpuLoad
Ordem:
descou qualquer outra string (o código trata como asc noelse)
Regras:
- se
jwt == nulloulogin == null⇒401 - se
jwt.subject != login⇒401 - se não existe volatile para esse uuid ⇒
404 - se o usuário não possui esse uuid em
systemsInPossession⇒401
Resposta:
- pega
SystemVolatileEntityPersistentdo banco - faz
readValue(sve.systemProcessEntities, SystemProcessEntity[].class) - ordena em memória conforme
sort - re-serializa a lista ordenada e seta de volta em
sve.systemProcessEntities - retorna
200comSystemVolatileEntityPersistent
Repare: o retorno é um SystemVolatileEntityPersistent, e o campo
systemProcessEntitiesé uma string JSON (porque na entidade éString @Lob). O controller apenas garante que essa string esteja ordenada conforme o sort solicitado.
Controller: HostToMetricsFrontendController.registerHandler
Auth:
Authorization: Bearer <jwtUser>
Header:
login: <string>
Query param:
uuid=<uuid do sistema>
Regras:
- se
login == nullou vazio ⇒400 - se usuário não existe ⇒
409 - se
jwt.subject != login⇒401 - se sistema não existe (
!ser.existsByUUID(uuid)) ⇒404 - se usuário já possui uuid ⇒
200
Ação:
- adiciona uuid em
systemsInPossessione salva usuário - retorna
200
Base: /api/user
Controller: HostToUserFrontendController.loginHandler
Entrada:
@RequestParam HashMap<String,String> loginInfo
Ou seja: form/query params, não JSON body.
Chaves exigidas:
loginpassword
Regras:
- params ausentes ⇒
400 - usuário não existe ⇒
404 - senha não confere (BCrypt) ⇒
401 - sucesso ⇒
200com token JWT (string)
Controller: HostToUserFrontendController.registerHandler
Entrada:
@RequestParam HashMap<String,String> loginInfo- chaves:
login,password
Regras:
- ausente ⇒
400 - vazio/whitespace ⇒
400 - se já existe ⇒
409 - salva
UserEntity(login, BCrypt(password)) - retorna
200com token JWT (string)
Controller: HostToUserFrontendController.removeUUIDFromUser
Header:
Authorization: Bearer <tokenUSER>
Query params:
user=<login>systemUUID=<uuid>
Comportamento:
-
se sistema existe e usuário possui uuid:
- remove da lista e salva
- retorna
200com{ "DeleteResult": true/false }(resultado doremove)
-
caso contrário:
- params ausentes ⇒
400 - usuário não existe ⇒
404 - JWT não autenticado ⇒
401 - retorna
404com{ "DeleteResult": false }
- params ausentes ⇒
- Provisionamento inicial
-
Agente manda
POST /api/systems/sysinfo- headers:
JwtToken: "null",RegisterToken: <RegisterKey> - body:
SystemEntityDTOcom"UUID": "null"
- headers:
-
Host responde com:
- UUID gerado
- token AGENT (60s)
- Atualização periódica do inventário “fixo”
-
Agente manda
POST /api/systems/sysinfo- headers:
JwtToken: <tokenAGENT>,RegisterToken: "null" - body:
SystemEntityDTOcom"UUID": "<uuid>"
- headers:
-
Host valida
uuid == subject(token)e salvaSystemEntityPersistent.
- Atualização periódica de métricas voláteis
-
Agente manda
POST /api/systems/sysinfovolatileAuthorization: Bearer <tokenAGENT>- body:
SystemVolatileEntityDTO
-
Host valida
uuid == subject(token)e salvaSystemVolatileEntityPersistent(stringificando os mapas/lista em JSON).
- Renovar token AGENT
-
Se token expirou, existe
GET /api/systems/updateAuth- headers:
JwtToken: "null",RegisterToken: <RegisterKey>,sysUUID: <uuid>
- headers:
-
Host retorna token novo.
POST /api/user/registerouPOST /api/user/login
- envia params
loginepassword - recebe token USER (3h)
- Vincular um sistema ao usuário (posse)
-
POST /api/metrics/systemRegister?uuid=<uuid>Authorization: Bearer <tokenUSER>- header
login: <login>
-
adiciona uuid em
systemsInPossession
- Listar sistemas do usuário
-
GET /api/metrics/systems?page=0&size=10Authorization: Bearer <tokenUSER>- header
login: <login>
-
retorna Page de
SystemEntityPersistent
- Consultar métricas voláteis (com ordenação de processos)
-
GET /api/metrics/systemVolatileInfo?uuid=<uuid>&sort=cpuLoad,descAuthorization: Bearer <tokenUSER>- header
login: <login>
-
retorna
SystemVolatileEntityPersistentcomsystemProcessEntitiesordenado (string JSON).
Existe:
-
resources/static/index.html+ assets -
resources/templates/index.html+ assets eWebControllerserve: -
GET /⇒ retorna"index"(template)
Então o Spring está configurado para servir uma página inicial.
O projeto inclui a dependência:
org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.15
E o SecurityConfig libera:
/swagger-ui.html/swagger-ui/**/v3/**