funifier-mcp 0.3.15 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/funifier.mdc +1 -0
- package/.github/copilot-instructions.md +1 -0
- package/AGENTS.md +1 -0
- package/datasource-funifier-docs/.coverage.json +10 -3
- package/datasource-funifier-docs/.search-index.json +10572 -10093
- package/datasource-funifier-docs/.skills-map.json +4 -0
- package/datasource-funifier-docs/.validation.json +39 -3
- package/datasource-funifier-docs/knowledge/index.md +1 -0
- package/datasource-funifier-docs/knowledge/modules/audit.md +357 -0
- package/dist/core/api-client.d.ts +4 -1
- package/dist/core/api-client.d.ts.map +1 -1
- package/dist/core/api-client.js +30 -0
- package/dist/core/api-client.js.map +1 -1
- package/dist/core/constants.d.ts +1 -0
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +2 -0
- package/dist/core/constants.js.map +1 -1
- package/dist/core/types/Audit.d.ts +25 -0
- package/dist/core/types/Audit.d.ts.map +1 -0
- package/dist/core/types/Audit.js +3 -0
- package/dist/core/types/Audit.js.map +1 -0
- package/dist/core/types/index.d.ts +1 -0
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/index.js +1 -0
- package/dist/core/types/index.js.map +1 -1
- package/dist/mcp/bundle.js +74 -74
- package/dist/mcp/tools/_fetch-current.d.ts +1 -1
- package/dist/mcp/tools/_fetch-current.d.ts.map +1 -1
- package/dist/mcp/tools/_fetch-current.js +3 -0
- package/dist/mcp/tools/_fetch-current.js.map +1 -1
- package/dist/mcp/tools/delete.d.ts.map +1 -1
- package/dist/mcp/tools/delete.js +4 -0
- package/dist/mcp/tools/delete.js.map +1 -1
- package/dist/mcp/tools/get.d.ts.map +1 -1
- package/dist/mcp/tools/get.js +4 -0
- package/dist/mcp/tools/get.js.map +1 -1
- package/dist/mcp/tools/list.d.ts.map +1 -1
- package/dist/mcp/tools/list.js +14 -0
- package/dist/mcp/tools/list.js.map +1 -1
- package/dist/mcp/tools/save.d.ts.map +1 -1
- package/dist/mcp/tools/save.js +3 -0
- package/dist/mcp/tools/save.js.map +1 -1
- package/dist/mcp/tools/save.test.js +12 -0
- package/dist/mcp/tools/save.test.js.map +1 -1
- package/package.json +2 -2
- package/skills/funifier/SKILL.md +1 -0
- package/skills/funifier/references/create-aggregate.md +3 -1
- package/skills/funifier/references/create-audit.md +115 -0
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"knowledge/modules/action.md",
|
|
31
31
|
"knowledge/modules/action-log.md"
|
|
32
32
|
],
|
|
33
|
+
"funifier-create-audit": [
|
|
34
|
+
"knowledge/modules/audit.md",
|
|
35
|
+
"knowledge/modules/database.md"
|
|
36
|
+
],
|
|
33
37
|
"funifier-create-point": [
|
|
34
38
|
"knowledge/modules/point.md",
|
|
35
39
|
"knowledge/modules/achievement.md"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"generatedAt": "2026-06-12T12:18:00Z",
|
|
3
3
|
"skills": {
|
|
4
4
|
"funifier-configure-security": {
|
|
5
5
|
"configHash": "fba0f846a53838e369f5c079104cd4a2f16a9d535a0320701d16287e53f35496",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"funifier-create-aggregate": {
|
|
36
|
-
"configHash": "
|
|
36
|
+
"configHash": "9598173385df039c369c8efc036ec22faaa3f9e56cb6c787498bf3d72b18a634",
|
|
37
37
|
"status": "verified",
|
|
38
38
|
"claims": [
|
|
39
39
|
{
|
|
@@ -48,6 +48,42 @@
|
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
|
+
"funifier-create-audit": {
|
|
52
|
+
"configHash": "17d53b67ecf952aaa352f0799e465d00de59960cd51cea31dee6f5d9ad5f5c8f",
|
|
53
|
+
"status": "verified",
|
|
54
|
+
"claims": [
|
|
55
|
+
{
|
|
56
|
+
"claim": "Audit config fields are _id, title, active, entity, event, clean, type with event constants create/update/delete and type constants studio/player/all",
|
|
57
|
+
"gitnexusQuery": "Audit",
|
|
58
|
+
"critical": true,
|
|
59
|
+
"status": "verified",
|
|
60
|
+
"evidence": {
|
|
61
|
+
"symbol": "Class:src/main/java/com/funifier/engine/audit/Audit.java:Audit",
|
|
62
|
+
"file": "src/main/java/com/funifier/engine/audit/Audit.java"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"claim": "AuditManager.log only writes an AuditLog when an active Audit matches entity+event and the config type matches the operation origin",
|
|
67
|
+
"gitnexusQuery": "AuditManager",
|
|
68
|
+
"critical": true,
|
|
69
|
+
"status": "verified",
|
|
70
|
+
"evidence": {
|
|
71
|
+
"symbol": "Class:src/main/java/com/funifier/engine/audit/AuditManager.java:AuditManager",
|
|
72
|
+
"file": "src/main/java/com/funifier/engine/audit/AuditManager.java"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"claim": "clearExpiredLogs purges audit_log entries older than each config's clean retention and is invoked from AsyncProcessor",
|
|
77
|
+
"gitnexusQuery": "clearExpiredLogs",
|
|
78
|
+
"critical": false,
|
|
79
|
+
"status": "verified",
|
|
80
|
+
"evidence": {
|
|
81
|
+
"symbol": "Method:src/main/java/com/funifier/engine/audit/AuditManager.java:AuditManager.clearExpiredLogs#0",
|
|
82
|
+
"file": "src/main/java/com/funifier/engine/audit/AuditManager.java"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
51
87
|
"funifier-create-challenge": {
|
|
52
88
|
"configHash": "45e50eab442325d3342dddff5d7c48f34002fe0182ed78c31a48fffdb8eafd03",
|
|
53
89
|
"status": "verified",
|
|
@@ -501,7 +537,7 @@
|
|
|
501
537
|
]
|
|
502
538
|
},
|
|
503
539
|
"funifier-help": {
|
|
504
|
-
"configHash": "
|
|
540
|
+
"configHash": "7af4ce568c8429bd33b797d070dfaed47ad08618ce1074c8532df0aa33fed1c8",
|
|
505
541
|
"status": "verified",
|
|
506
542
|
"claims": [
|
|
507
543
|
{
|
|
@@ -86,6 +86,7 @@ A Funifier funciona como um **backend de aplicações**. Independentemente do ti
|
|
|
86
86
|
|--------|----------|-----------|-------------|
|
|
87
87
|
| Auth | `modules/auth.md` | Autenticação e tokens | Gerar tokens de acesso; configurar API keys; autenticação OAuth |
|
|
88
88
|
| Security | `modules/security.md` | Configuração de segurança | Configurar apps (app_secret, scope, whitelist) e roles de jogador; entender escopos hierárquicos, pipeline de autorização e emissão de tokens; diagnóstico de erros 401 |
|
|
89
|
+
| Audit | `modules/audit.md` | Auditoria de operações | Registrar e consultar quem criou/alterou/removeu jogadores ou documentos de coleções (entity/event/type/retenção); montar trilhas de auditoria robustas e relatórios de "quem fez o quê" |
|
|
89
90
|
| Folder | `modules/folder.md` | Trilhas e cursos | Criar trilhas de aprendizagem e cursos; organizar conteúdos em hierarquia com monitoramento de progresso do jogador |
|
|
90
91
|
| Backup | `modules/backup.md` | Backup e restauração | Criar backups da gamificação; restaurar configurações |
|
|
91
92
|
| Compact | `modules/compact.md` | Compactação de dados | Compactar dados históricos para otimizar performance |
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# `audit`
|
|
2
|
+
|
|
3
|
+
**Acesso Studio:** `/studio/audit` _(convenção de UI)_
|
|
4
|
+
**API Endpoint:** `/v3/audit` (`com.funifier.rest.v3.rest.AuditRest`)
|
|
5
|
+
**Coleções MongoDB:** `audit` (configuração) e `audit_log` (registros)
|
|
6
|
+
|
|
7
|
+
> Documento de engenharia reversa baseado **exclusivamente** no código de `funifier-service` (`com.funifier.engine.audit`) e `funifier-studio` (`app/scripts/controllers/studio/audit`). Cobre a **configuração de auditoria** (entidade `Audit`, coleção `audit`) e o **registro de eventos** (entidade `AuditLog`, coleção `audit_log`).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Visão Geral
|
|
12
|
+
|
|
13
|
+
A auditoria do Funifier tem **dois objetos distintos**:
|
|
14
|
+
|
|
15
|
+
1. **`Audit` (configuração)** — declara *o que* rastrear: uma combinação `entity` + `event` (ex.: `player` + `update`). É um cadastro, criado via `POST /v3/audit`. Sem uma config `Audit` **ativa** para um par `entity`/`event`, nenhum log é gravado.
|
|
16
|
+
2. **`AuditLog` (registro)** — é a *ocorrência* gravada quando uma operação rastreada acontece. Guarda *quem* (`user`/`login`), *quando* (`time`), o *objeto* afetado (`item`) e a *origem* (`type`: `studio`/`player`). Vive na coleção `audit_log`.
|
|
17
|
+
|
|
18
|
+
Papel arquitetural:
|
|
19
|
+
|
|
20
|
+
- **Trilha de auditoria sob demanda.** A auditoria é *opt-in* por par `entity`/`event` — diferente de um log global. Você liga só o que quer.
|
|
21
|
+
- **Retenção automática.** Cada config tem um campo `clean` (ex.: `"30d"`); um job periódico (`AuditManager.clearExpiredLogs`, disparado pelo `AsyncProcessor`) remove os `audit_log` mais velhos que a retenção da sua config.
|
|
22
|
+
- **Filtro por origem.** O campo `type` (`studio`/`player`/`all`) decide se logam operações vindas do Studio (usuário admin autenticado), do Player (token de jogador) ou ambas.
|
|
23
|
+
|
|
24
|
+
> ⚠️ **Cobertura limitada de gatilhos.** Hoje, somente dois pontos do código chamam `AuditManager.log`: o cadastro/atualização de **player** (`PlayerManager`) e as operações de **database** (`DatabaseRest`: create/update/delete em qualquer coleção). Criar uma config para `entity:"challenge"` + `event:"create"` é aceito, mas **nunca produzirá logs** — challenge não dispara auditoria. Vide seção 7.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Arquitetura e Fluxos
|
|
29
|
+
|
|
30
|
+
### 2.1 Classes envolvidas
|
|
31
|
+
|
|
32
|
+
| Classe | Papel |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `com.funifier.engine.audit.Audit` | Entidade/POJO da configuração (coleção `audit`). |
|
|
35
|
+
| `com.funifier.engine.audit.AuditLog` | Entidade/POJO do registro (coleção `audit_log`). |
|
|
36
|
+
| `com.funifier.engine.audit.AuditManager` | Manager: `save`, `find`, `delete` (cascata), `log`, `clearExpiredLogs`. |
|
|
37
|
+
| `com.funifier.rest.v3.rest.AuditRest` | Controller REST v3 (`/v3/audit`). |
|
|
38
|
+
| `com.funifier.controller.AsyncProcessor` | Dispara `clearExpiredLogs` periodicamente (limpeza de retenção). |
|
|
39
|
+
|
|
40
|
+
`Audit` e `AuditLog` usam `@JsonIgnoreProperties(ignoreUnknown=true)` — **campos desconhecidos são descartados** na desserialização.
|
|
41
|
+
|
|
42
|
+
### 2.2 Quem dispara um log — `AuditManager.log(entity, event, auth, item)`
|
|
43
|
+
|
|
44
|
+
A gravação só acontece se **todas** as condições forem satisfeitas (`AuditManager.log`):
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
[1] entity, event e auth não nulos
|
|
48
|
+
[2] determina a origem:
|
|
49
|
+
user = auth.getUserId() // Studio (admin)
|
|
50
|
+
player = auth.getPlayerFromTokenIfExist() // Player
|
|
51
|
+
type = user != null ? "studio"
|
|
52
|
+
: player != null ? "player"
|
|
53
|
+
: null → se null, NÃO loga
|
|
54
|
+
[3] busca config ATIVA: audit.findOne({active:true, entity:#, event:#})
|
|
55
|
+
→ se não existe config ativa para esse entity/event, NÃO loga
|
|
56
|
+
[4] checa o filtro de origem da config:
|
|
57
|
+
type_da_config == "all"
|
|
58
|
+
OR (type_da_config == "studio" E user != null)
|
|
59
|
+
OR (type_da_config == "player" E player != null)
|
|
60
|
+
[5] grava AuditLog { audit, time, item, type, user, login }
|
|
61
|
+
(studio → resolve login via UserStudioManager; player → login = id do player)
|
|
62
|
+
[6] também imprime no stdout: "AUDIT - Title : ... - User : ... - Item : ..."
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```mermaid
|
|
66
|
+
flowchart TD
|
|
67
|
+
A["operação rastreável<br/>(player ou database CRUD)"] --> B["AuditManager.log(entity, event, auth, item)"]
|
|
68
|
+
B --> C{"origem identificável?<br/>(studio ou player)"}
|
|
69
|
+
C -- não --> X[não loga]
|
|
70
|
+
C -- sim --> D{"existe Audit<br/>active:true,<br/>entity, event?"}
|
|
71
|
+
D -- não --> X
|
|
72
|
+
D -- sim --> E{"type da config<br/>combina com a origem?"}
|
|
73
|
+
E -- não --> X
|
|
74
|
+
E -- sim --> F["grava audit_log<br/>+ stdout"]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2.3 Pontos que chamam `log` (gatilhos atuais)
|
|
78
|
+
|
|
79
|
+
| Origem | Onde | Chamada |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| Player — criação | `PlayerManager:296` | `log(Entity.PLAYER.collection, EVENT_CREATE, authBean, player)` |
|
|
82
|
+
| Player — atualização | `PlayerManager:306` | `log(Entity.PLAYER.collection, EVENT_UPDATE, authBean, player)` |
|
|
83
|
+
| Database — insert | `DatabaseRest:233` | `log(collection, EVENT_CREATE, authBean, o)` |
|
|
84
|
+
| Database — update | `DatabaseRest:377` | `log(collection, EVENT_UPDATE, authBean, o)` |
|
|
85
|
+
| Database — delete | `DatabaseRest:488` | `log(collection, EVENT_DELETE, authBean, ids)` |
|
|
86
|
+
|
|
87
|
+
> No delete de database, `item` é o **conjunto de ids removidos**, não o documento completo.
|
|
88
|
+
|
|
89
|
+
### 2.4 Retenção — `clearExpiredLogs` (cron)
|
|
90
|
+
|
|
91
|
+
`AsyncProcessor` chama periodicamente `AuditManager.clearExpiredLogs`, que, para **cada** config `Audit`, remove de `audit_log` os registros onde `time <= (agora - clean)`:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
para cada Audit:
|
|
95
|
+
expire = agora - audit.clean // ex.: clean "30d" → -30 dias
|
|
96
|
+
db.audit_log.remove({audit: audit._id, time: {$lte: expire}})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Ou seja, a retenção é **por config** — cada par `entity`/`event` pode ter um `clean` diferente.
|
|
100
|
+
|
|
101
|
+
### 2.5 Exclusão — `DELETE /v3/audit/:id` (cascata)
|
|
102
|
+
|
|
103
|
+
`AuditManager.delete` remove a config **e todos os seus logs**:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
db.audit.remove({_id: id})
|
|
107
|
+
db.audit_log.remove({audit: id}) // cascata
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 3. Estrutura dos Objetos
|
|
113
|
+
|
|
114
|
+
### 3.1 `Audit` — configuração (coleção `audit`)
|
|
115
|
+
|
|
116
|
+
| Campo | Tipo | Padrão | Descrição |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| `_id` | String | auto (`Guid.shortTimeMillis()`) | ID da config. Se omitido, gerado no save. No Studio, ao informar, é saneado para `[A-Za-z0-9_]`. |
|
|
119
|
+
| `title` | String | — | Rótulo legível (ex.: `"Alteração de jogadores"`). Usado nos logs do stdout. |
|
|
120
|
+
| `active` | boolean | `true` | Só configs `active:true` produzem logs (`log` filtra por `active:true`). |
|
|
121
|
+
| `entity` | String | — | Nome da coleção/entidade rastreada (ex.: `"player"`). |
|
|
122
|
+
| `event` | String | — | `"create"`, `"update"` ou `"delete"` (constantes `EVENT_CREATE/UPDATE/DELETE`). |
|
|
123
|
+
| `clean` | String | `"30d"` | Retenção dos logs. Se ausente/vazio no save, o backend assume `"30d"`. |
|
|
124
|
+
| `type` | String | `"all"` | `"studio"`, `"player"` ou `"all"` (constantes `TYPE_STUDIO/PLAYER/ALL`). Filtra a origem. |
|
|
125
|
+
| `created` | Date | auto | Preenchido no primeiro save. |
|
|
126
|
+
| `updated` | Date | auto | Atualizado a cada save. |
|
|
127
|
+
|
|
128
|
+
**Comportamento de save (`AuditManager.save`):**
|
|
129
|
+
|
|
130
|
+
- `_id` ausente → gera `Guid.shortTimeMillis()` e define `created`.
|
|
131
|
+
- `clean` ausente/vazio → assume `"30d"`.
|
|
132
|
+
- `updated` sempre redefinido. `c.save` é **upsert por `_id`** (full replace).
|
|
133
|
+
|
|
134
|
+
### 3.2 `AuditLog` — registro (coleção `audit_log`)
|
|
135
|
+
|
|
136
|
+
| Campo | Tipo | Descrição |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `_id` | String | ID do registro (`Guid.newShortGuid()`). |
|
|
139
|
+
| `audit` | String | `_id` da config `Audit` que originou o log. |
|
|
140
|
+
| `user` | String | ID do ator (id do usuário Studio **ou** id do player). |
|
|
141
|
+
| `login` | String | Login do ator (resolvido via `UserStudioManager` para studio; = id para player). |
|
|
142
|
+
| `time` | Date | Momento do evento. |
|
|
143
|
+
| `item` | Object | Objeto auditado (documento) — ou **lista de ids** no delete de database. |
|
|
144
|
+
| `type` | String | Origem: `"studio"` ou `"player"`. |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 4. Endpoints
|
|
149
|
+
|
|
150
|
+
Todos sob `AuditRest` (`@Path("v3/audit")`). Autenticação: Bearer token (`AuthBean`).
|
|
151
|
+
|
|
152
|
+
### `POST /v3/audit`
|
|
153
|
+
|
|
154
|
+
| Aspecto | Detalhe |
|
|
155
|
+
|---|---|
|
|
156
|
+
| Finalidade | Criar **ou** atualizar uma config de auditoria. |
|
|
157
|
+
| Full replace ou patch | **Full replace** (upsert por `_id` via `c.save`). |
|
|
158
|
+
| Status | `201 CREATED`, retorna a config. |
|
|
159
|
+
| Comportamento real | Gera `_id`/`created` se ausentes; assume `clean:"30d"` se ausente; atualiza `updated`. |
|
|
160
|
+
|
|
161
|
+
**Exemplo:**
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"title": "Alteração de jogadores",
|
|
166
|
+
"active": true,
|
|
167
|
+
"entity": "player",
|
|
168
|
+
"event": "update",
|
|
169
|
+
"type": "all",
|
|
170
|
+
"clean": "90d"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `GET /v3/audit/:id`
|
|
175
|
+
|
|
176
|
+
| Aspecto | Detalhe |
|
|
177
|
+
|---|---|
|
|
178
|
+
| Finalidade | Buscar uma config por `_id` exato (`AuditManager.find`). |
|
|
179
|
+
| Resposta | A `Audit` (campos nulos removidos). |
|
|
180
|
+
|
|
181
|
+
### `DELETE /v3/audit/:id`
|
|
182
|
+
|
|
183
|
+
| Aspecto | Detalhe |
|
|
184
|
+
|---|---|
|
|
185
|
+
| Finalidade | Excluir a config. |
|
|
186
|
+
| Status | `204 NO CONTENT`. |
|
|
187
|
+
| Comportamento real | **Cascata**: remove a config **e todos** os `audit_log` com `audit == id`. |
|
|
188
|
+
|
|
189
|
+
### Consulta de logs — **não há endpoint dedicado**
|
|
190
|
+
|
|
191
|
+
`audit_log` é uma coleção comum; consulte-a pelo módulo `database` (vide `database.md`):
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
POST /v3/database/audit_log/aggregate
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Pipeline típico (espelha o Studio — `controllers/studio/audit/log/list.js`):
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
[
|
|
201
|
+
{ "$match": { "audit": "<audit_id>" } },
|
|
202
|
+
{ "$match": { "time": { "$gte": { "$date": "-30d-" } } } },
|
|
203
|
+
{ "$sort": { "time": -1 } }
|
|
204
|
+
]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Filtros úteis (todos opcionais, combináveis em `$match`):
|
|
208
|
+
|
|
209
|
+
| Quero filtrar por… | `$match` |
|
|
210
|
+
|---|---|
|
|
211
|
+
| Config específica | `{ "audit": "<audit_id>" }` |
|
|
212
|
+
| Ator (id ou login) | `{ "user": "<id>" }` ou `{ "login": "<login>" }` |
|
|
213
|
+
| Origem | `{ "type": "studio" }` (ou `"player"`) |
|
|
214
|
+
| Janela de tempo | `{ "time": { "$gte": {"$date": "..."}, "$lte": {"$date": "..."} } }` |
|
|
215
|
+
| Campo do objeto auditado | `{ "item.<campo>": <valor> }` |
|
|
216
|
+
|
|
217
|
+
> Para sintaxe de datas relativas/absolutas (`-30d-`, `$date`), veja `date-handling.md`.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 5. Regras de Negócio
|
|
222
|
+
|
|
223
|
+
1. **Auditoria é opt-in por par `entity`/`event`.** Sem config ativa, nada é logado.
|
|
224
|
+
2. **`active:false` desliga.** A query de `log` exige `active:true`.
|
|
225
|
+
3. **Filtro de origem (`type`).** `studio` loga só operações de admin; `player` só de jogador; `all` ambas. Operações sem ator identificável (nem user nem player) **nunca** logam.
|
|
226
|
+
4. **Retenção por config.** `clean` é avaliado por config no cron; `"30d"` é o default do backend.
|
|
227
|
+
5. **POST é upsert.** Reenviar com o mesmo `_id` substitui a config inteira.
|
|
228
|
+
6. **Delete cascateia.** Remover a config apaga seus logs.
|
|
229
|
+
7. **Multi-tenant.** Cada gamificação (apiKey) isola suas coleções `audit`/`audit_log`.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 6. Comportamentos Automáticos
|
|
234
|
+
|
|
235
|
+
| Comportamento | Trigger | Impacto |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| Geração de `_id`/`created` | POST sem `_id` | `Guid.shortTimeMillis()` + `created` |
|
|
238
|
+
| Default de `clean` | POST sem `clean` | assume `"30d"` |
|
|
239
|
+
| Atualização de `updated` | todo POST | redefine `updated` |
|
|
240
|
+
| Gravação de `audit_log` | player/database CRUD com config ativa compatível | novo registro + linha no stdout |
|
|
241
|
+
| Limpeza por retenção | cron (`AsyncProcessor`) | remove logs com `time <= agora - clean` |
|
|
242
|
+
| Cascata de exclusão | DELETE da config | apaga config **e** todos os seus logs |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 7. Suportado vs NÃO Suportado
|
|
247
|
+
|
|
248
|
+
### ✅ Suportado
|
|
249
|
+
|
|
250
|
+
- CRUD de configs: `POST /v3/audit`, `GET /v3/audit/:id`, `DELETE /v3/audit/:id` (cascata).
|
|
251
|
+
- Auditoria de **player** (`create`/`update`) e de **database** (`create`/`update`/`delete` em qualquer coleção).
|
|
252
|
+
- Filtro de origem por `type` (`studio`/`player`/`all`).
|
|
253
|
+
- Retenção automática por config via `clean`.
|
|
254
|
+
- Consulta de logs por `database/audit_log/aggregate` com filtros de tempo, ator, origem e campos do `item`.
|
|
255
|
+
|
|
256
|
+
### ❌ NÃO Suportado / Armadilhas
|
|
257
|
+
|
|
258
|
+
- **Auditoria de outras entidades** (challenge, level, action, virtual good, etc.) — **não há gatilho**. Só `player` e operações de `database` chamam `log`. Uma config para outra `entity` é inerte.
|
|
259
|
+
- **Endpoint de listagem de configs** — não existe `GET /v3/audit` (lista). Liste via `database/audit/aggregate`.
|
|
260
|
+
- **Endpoint dedicado de logs** — não existe; use `database/audit_log`.
|
|
261
|
+
- **Patch parcial** — POST é full replace (upsert).
|
|
262
|
+
- **`PUT`** — não existe; toda escrita é via POST.
|
|
263
|
+
- **Auditoria de leitura (read)** — só `create`/`update`/`delete`; não há evento de leitura.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 8. Observabilidade e Troubleshooting
|
|
268
|
+
|
|
269
|
+
**Listar configs existentes:**
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
POST /v3/database/audit/aggregate body: []
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Contar logs por config (como o Studio faz):**
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
[
|
|
279
|
+
{ "$lookup": {
|
|
280
|
+
"from": "audit_log",
|
|
281
|
+
"let": { "audit": "$_id" },
|
|
282
|
+
"pipeline": [
|
|
283
|
+
{ "$match": { "$expr": { "$eq": ["$audit", "$$audit"] } } },
|
|
284
|
+
{ "$group": { "_id": "$audit", "total": { "$sum": 1 } } }
|
|
285
|
+
],
|
|
286
|
+
"as": "logs"
|
|
287
|
+
} }
|
|
288
|
+
]
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**"Configurei a auditoria mas não aparece nenhum log":**
|
|
292
|
+
|
|
293
|
+
- A `entity` é `player` ou uma coleção manipulada via `database`? Outras entidades **não disparam** auditoria.
|
|
294
|
+
- A config está `active:true`?
|
|
295
|
+
- O `type` combina com a origem da operação? (operação de jogador não loga numa config `type:"studio"`.)
|
|
296
|
+
- A operação tinha um ator autenticado? Operações sem user/player não logam.
|
|
297
|
+
|
|
298
|
+
**"Os logs sumiram":** a retenção (`clean`) pode tê-los expirado; ou a config foi deletada (cascata apaga os logs).
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 9. Exemplos Práticos
|
|
303
|
+
|
|
304
|
+
### 9.1 Auditar todas as alterações de jogadores, retidas por 90 dias
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"title": "Alteração de jogadores",
|
|
309
|
+
"active": true,
|
|
310
|
+
"entity": "player",
|
|
311
|
+
"event": "update",
|
|
312
|
+
"type": "all",
|
|
313
|
+
"clean": "90d"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 9.2 Auditar exclusões numa coleção de negócio feitas pelo Studio
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"title": "Exclusões em pedidos (admin)",
|
|
322
|
+
"active": true,
|
|
323
|
+
"entity": "order__c",
|
|
324
|
+
"event": "delete",
|
|
325
|
+
"type": "studio",
|
|
326
|
+
"clean": "180d"
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 9.3 Anti-pattern — o que NÃO fazer
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"title": "Auditar criação de desafios",
|
|
335
|
+
"entity": "challenge",
|
|
336
|
+
"event": "create"
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Por quê está errado:
|
|
341
|
+
|
|
342
|
+
- `entity:"challenge"` → **não há gatilho** de auditoria para challenge; a config nunca gera logs.
|
|
343
|
+
- `clean` omitido → assume `"30d"` silenciosamente (defina explicitamente para não perder logs antes do esperado).
|
|
344
|
+
- `type` omitido → assume `"all"` (ok, mas seja deliberado).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Checklist de Configuração
|
|
349
|
+
|
|
350
|
+
- [ ] `entity` é `player` **ou** uma coleção manipulada via `database` (senão a config é inerte).
|
|
351
|
+
- [ ] `event` ∈ `create` | `update` | `delete`.
|
|
352
|
+
- [ ] `active: true`.
|
|
353
|
+
- [ ] `type` escolhido deliberadamente (`studio` | `player` | `all`) conforme a origem a rastrear.
|
|
354
|
+
- [ ] `clean` definido **explicitamente** (não confie no default `30d`); ciente da limpeza por cron.
|
|
355
|
+
- [ ] `_id`/`title` legíveis (`_id` saneado para `[A-Za-z0-9_]` no Studio).
|
|
356
|
+
- [ ] Consulta de logs planejada via `database/audit_log/aggregate` (`$match audit/time`, `$sort time:-1`).
|
|
357
|
+
- [ ] Ciente de que `DELETE` da config apaga **todos** os logs em cascata.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FunifierConfig } from "./config";
|
|
2
|
-
import { Trigger, Scheduler, Aggregate, Widget, CustomPage, PublicEndpoint, ChallengeAggregate, AiKnowledge, AuthModule, Websocket, Action, Challenge, Point, Level, LevelConfig, Leaderboard, LeaderboardEntry, Quiz, Question, VirtualGoodCatalog, VirtualGoodItem, Folder, FolderContent, FolderContentType, FolderLog, Role, PrincipalRole } from "./types";
|
|
2
|
+
import { Trigger, Scheduler, Aggregate, Widget, CustomPage, PublicEndpoint, ChallengeAggregate, AiKnowledge, AuthModule, Websocket, Action, Challenge, Point, Level, LevelConfig, Leaderboard, LeaderboardEntry, Quiz, Question, VirtualGoodCatalog, VirtualGoodItem, Folder, FolderContent, FolderContentType, FolderLog, Role, PrincipalRole, Audit } from "./types";
|
|
3
3
|
export declare function serializeToFunifierQuery(filter: Record<string, any>): string;
|
|
4
4
|
export declare function createAPIClient(config: FunifierConfig): {
|
|
5
5
|
listTriggers: () => Promise<Trigger[]>;
|
|
@@ -100,6 +100,9 @@ export declare function createAPIClient(config: FunifierConfig): {
|
|
|
100
100
|
listActions: () => Promise<Action[]>;
|
|
101
101
|
saveAction: (payload: Action) => Promise<Action>;
|
|
102
102
|
deleteAction: (id: string) => Promise<any>;
|
|
103
|
+
listAudits: () => Promise<Audit[]>;
|
|
104
|
+
saveAudit: (payload: Audit) => Promise<Audit>;
|
|
105
|
+
deleteAudit: (id: string) => Promise<any>;
|
|
103
106
|
listChallenges: () => Promise<Challenge[]>;
|
|
104
107
|
saveChallenge: (payload: Challenge) => Promise<Challenge>;
|
|
105
108
|
deleteChallenge: (id: string) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/core/api-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EACL,OAAO,EACP,SAAS,EACT,SAAS,EACT,MAAM,EACN,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAS,EACT,KAAK,EACL,KAAK,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,IAAI,EACJ,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,SAAS,EACT,IAAI,EACJ,aAAa,
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/core/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/core/api-client.js
CHANGED
|
@@ -744,6 +744,36 @@ function createAPIClient(config) {
|
|
|
744
744
|
throw new Error("Failed to delete action: " + extractErrorMessage(e));
|
|
745
745
|
}
|
|
746
746
|
},
|
|
747
|
+
// --- Audit ---
|
|
748
|
+
// No list REST endpoint exists; read all configs via aggregate on the "audit" collection.
|
|
749
|
+
listAudits: async () => {
|
|
750
|
+
try {
|
|
751
|
+
const response = await axiosClient.post("/database/audit/aggregate", []);
|
|
752
|
+
return response.data;
|
|
753
|
+
}
|
|
754
|
+
catch (e) {
|
|
755
|
+
console.error(e);
|
|
756
|
+
return [];
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
saveAudit: async (payload) => {
|
|
760
|
+
try {
|
|
761
|
+
const response = await axiosClient.post(constants_1.Endpoints.AUDIT, payload);
|
|
762
|
+
return response.data;
|
|
763
|
+
}
|
|
764
|
+
catch (e) {
|
|
765
|
+
throw new Error("Failed to save audit: " + extractErrorMessage(e));
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
deleteAudit: async (id) => {
|
|
769
|
+
try {
|
|
770
|
+
const response = await axiosClient.delete(`${constants_1.Endpoints.AUDIT}/${id}`);
|
|
771
|
+
return response.data;
|
|
772
|
+
}
|
|
773
|
+
catch (e) {
|
|
774
|
+
throw new Error("Failed to delete audit: " + extractErrorMessage(e));
|
|
775
|
+
}
|
|
776
|
+
},
|
|
747
777
|
// --- Challenges ---
|
|
748
778
|
listChallenges: async () => {
|
|
749
779
|
try {
|