taskmanager-engine 0.1.0__py3-none-any.whl
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.
- taskmanager/__init__.py +150 -0
- taskmanager/api/app.py +488 -0
- taskmanager/api/events.py +68 -0
- taskmanager/cli.py +291 -0
- taskmanager/config.py +19 -0
- taskmanager/contrib/__init__.py +3 -0
- taskmanager/contrib/django/__init__.py +6 -0
- taskmanager/contrib/django/apps.py +46 -0
- taskmanager/contrib/django/management/__init__.py +1 -0
- taskmanager/contrib/django/management/commands/__init__.py +1 -0
- taskmanager/contrib/django/management/commands/run_scheduler.py +44 -0
- taskmanager/contrib/django/management/commands/run_worker.py +72 -0
- taskmanager/contrib/django/urls.py +30 -0
- taskmanager/contrib/fastapi.py +31 -0
- taskmanager/core/broker.py +541 -0
- taskmanager/core/builtin_tasks.py +59 -0
- taskmanager/core/job.py +50 -0
- taskmanager/core/task.py +224 -0
- taskmanager/scheduler/cron.py +52 -0
- taskmanager/scheduler/scheduler.py +178 -0
- taskmanager/ui/app.js +1390 -0
- taskmanager/ui/index.html +681 -0
- taskmanager/ui/styles.css +1048 -0
- taskmanager/worker/heartbeat.py +150 -0
- taskmanager/worker/worker.py +207 -0
- taskmanager_engine-0.1.0.dist-info/METADATA +284 -0
- taskmanager_engine-0.1.0.dist-info/RECORD +30 -0
- taskmanager_engine-0.1.0.dist-info/WHEEL +5 -0
- taskmanager_engine-0.1.0.dist-info/entry_points.txt +2 -0
- taskmanager_engine-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>TaskManager Dashboard</title>
|
|
7
|
+
<link rel="stylesheet" href="static/styles.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="app-container">
|
|
11
|
+
<!-- Navbar -->
|
|
12
|
+
<header class="navbar">
|
|
13
|
+
<div class="brand">
|
|
14
|
+
<div class="brand-icon">
|
|
15
|
+
<svg viewBox="0 0 24 24">
|
|
16
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
|
17
|
+
</svg>
|
|
18
|
+
</div>
|
|
19
|
+
<span>TaskManager</span>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<nav class="nav-tabs">
|
|
23
|
+
<button class="nav-tab active" data-tab="overview">
|
|
24
|
+
<span>Visão Geral</span>
|
|
25
|
+
</button>
|
|
26
|
+
<button class="nav-tab" data-tab="workers">
|
|
27
|
+
<span>Workers</span>
|
|
28
|
+
</button>
|
|
29
|
+
<button class="nav-tab" data-tab="queues">
|
|
30
|
+
<span>Filas & Jobs</span>
|
|
31
|
+
</button>
|
|
32
|
+
<button class="nav-tab" data-tab="schedules">
|
|
33
|
+
<span>Cron & Agendamentos</span>
|
|
34
|
+
</button>
|
|
35
|
+
<button class="nav-tab" data-tab="dlq">
|
|
36
|
+
<span>Dead Letter (DLQ)</span>
|
|
37
|
+
</button>
|
|
38
|
+
<button class="nav-tab" data-tab="history">
|
|
39
|
+
<span>📊 Execuções & Métricas</span>
|
|
40
|
+
</button>
|
|
41
|
+
</nav>
|
|
42
|
+
|
|
43
|
+
<div class="nav-actions">
|
|
44
|
+
<button class="btn-command-palette" onclick="openCommandPalette()" title="Busca & Comandos (Ctrl+K)">
|
|
45
|
+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
46
|
+
<span class="cmd-text">Buscar ou Comandos</span>
|
|
47
|
+
<kbd class="kbd-badge">Ctrl K</kbd>
|
|
48
|
+
</button>
|
|
49
|
+
|
|
50
|
+
<div class="ws-status">
|
|
51
|
+
<span class="status-dot" id="wsDot"></span>
|
|
52
|
+
<span id="wsText">Conectando...</span>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<button class="btn-icon" onclick="openMaintenanceModal()" title="Limpeza & Manutenção do Redis">
|
|
56
|
+
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
57
|
+
<circle cx="12" cy="12" r="3"></circle>
|
|
58
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
59
|
+
</svg>
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<div class="dropdown" id="dropdown-create">
|
|
63
|
+
<button class="btn btn-primary" onclick="toggleDropdown('dropdown-create-menu')">
|
|
64
|
+
<span>+ Criar</span>
|
|
65
|
+
<svg class="dropdown-chevron" viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M4.5 6l3.5 3.5 3.5-3.5z"/></svg>
|
|
66
|
+
</button>
|
|
67
|
+
<div class="dropdown-menu" id="dropdown-create-menu">
|
|
68
|
+
<button class="dropdown-item" onclick="openEnqueueModal(); closeDropdowns();">
|
|
69
|
+
<span class="dropdown-icon">⚡</span>
|
|
70
|
+
<div>
|
|
71
|
+
<div class="dropdown-title">Nova Tarefa</div>
|
|
72
|
+
<div class="dropdown-desc">Enfileirar execução imediata ou com delay</div>
|
|
73
|
+
</div>
|
|
74
|
+
</button>
|
|
75
|
+
<button class="dropdown-item" onclick="openScheduleModal(); closeDropdowns();">
|
|
76
|
+
<span class="dropdown-icon">⏰</span>
|
|
77
|
+
<div>
|
|
78
|
+
<div class="dropdown-title">Novo Cron / Agendamento</div>
|
|
79
|
+
<div class="dropdown-desc">Programar execução periódica ou intervalo</div>
|
|
80
|
+
</div>
|
|
81
|
+
</button>
|
|
82
|
+
<button class="dropdown-item" onclick="openCreateQueueModal(); closeDropdowns();">
|
|
83
|
+
<span class="dropdown-icon">📦</span>
|
|
84
|
+
<div>
|
|
85
|
+
<div class="dropdown-title">Nova Fila</div>
|
|
86
|
+
<div class="dropdown-desc">Registrar uma nova fila no Redis</div>
|
|
87
|
+
</div>
|
|
88
|
+
</button>
|
|
89
|
+
<div class="dropdown-divider"></div>
|
|
90
|
+
<button class="dropdown-item" onclick="openSpawnWorkerModal(); closeDropdowns();">
|
|
91
|
+
<span class="dropdown-icon">🤖</span>
|
|
92
|
+
<div>
|
|
93
|
+
<div class="dropdown-title">Iniciar Novo Worker</div>
|
|
94
|
+
<div class="dropdown-desc">Spawnar processo de worker dinâmico</div>
|
|
95
|
+
</div>
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</header>
|
|
101
|
+
|
|
102
|
+
<!-- Main Content -->
|
|
103
|
+
<main class="main-content">
|
|
104
|
+
<!-- 1. Overview Tab -->
|
|
105
|
+
<section id="tab-overview" class="tab-pane active">
|
|
106
|
+
<div class="metrics-grid">
|
|
107
|
+
<div class="metric-card">
|
|
108
|
+
<div class="metric-label">Workers Ativos</div>
|
|
109
|
+
<div class="metric-value" id="m-workers">0</div>
|
|
110
|
+
<div class="metric-sub" id="m-workers-sub">0 total registrados</div>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="metric-card">
|
|
113
|
+
<div class="metric-label">Jobs em Execução</div>
|
|
114
|
+
<div class="metric-value" id="m-active-jobs">0</div>
|
|
115
|
+
<div class="metric-sub">Processando agora</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="metric-card">
|
|
118
|
+
<div class="metric-label">Jobs Pendentes</div>
|
|
119
|
+
<div class="metric-value" id="m-pending-jobs">0</div>
|
|
120
|
+
<div class="metric-sub">Na fila FIFO</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="metric-card">
|
|
123
|
+
<div class="metric-label">Jobs Agendados</div>
|
|
124
|
+
<div class="metric-value" id="m-delayed-jobs">0</div>
|
|
125
|
+
<div class="metric-sub">Aguardando timestamp</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="metric-card">
|
|
128
|
+
<div class="metric-label">Dead Letter (DLQ)</div>
|
|
129
|
+
<div class="metric-value" id="m-dlq-jobs" style="color: var(--semantic-error);">0</div>
|
|
130
|
+
<div class="metric-sub">Falhas esgotadas</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="metric-card">
|
|
133
|
+
<div class="metric-label">Crons Ativos</div>
|
|
134
|
+
<div class="metric-value" id="m-schedules">0</div>
|
|
135
|
+
<div class="metric-sub">Rotinas agendadas</div>
|
|
136
|
+
</div>
|
|
137
|
+
<div class="metric-card">
|
|
138
|
+
<div class="metric-label">CPU (Workers)</div>
|
|
139
|
+
<div class="metric-value" id="m-cpu">0.0%</div>
|
|
140
|
+
<div class="metric-sub" id="m-cpu-sub">Processos worker ativos</div>
|
|
141
|
+
<div class="metric-progress">
|
|
142
|
+
<div class="metric-progress-fill" id="m-cpu-bar" style="width: 0%;"></div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="metric-card">
|
|
146
|
+
<div class="metric-label">Memória RSS (Workers)</div>
|
|
147
|
+
<div class="metric-value" id="m-memory">0.0 MB</div>
|
|
148
|
+
<div class="metric-sub" id="m-memory-sub">Consumo direto dos workers</div>
|
|
149
|
+
<div class="metric-progress">
|
|
150
|
+
<div class="metric-progress-fill" id="m-memory-bar" style="width: 0%;"></div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div class="card">
|
|
156
|
+
<div class="card-header">
|
|
157
|
+
<h2 class="card-title">Status das Filas</h2>
|
|
158
|
+
<div style="display: flex; gap: 8px;">
|
|
159
|
+
<button class="btn btn-secondary btn-sm" onclick="openCreateQueueModal()">+ Nova Fila</button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="card-body table-container">
|
|
163
|
+
<table>
|
|
164
|
+
<thead>
|
|
165
|
+
<tr>
|
|
166
|
+
<th>Fila</th>
|
|
167
|
+
<th>Pendentes</th>
|
|
168
|
+
<th>Agendados</th>
|
|
169
|
+
<th>DLQ</th>
|
|
170
|
+
<th>Ações</th>
|
|
171
|
+
</tr>
|
|
172
|
+
</thead>
|
|
173
|
+
<tbody id="overview-queues-table">
|
|
174
|
+
<tr><td colspan="5" style="text-align: center; color: var(--ink-subtle);">Carregando filas...</td></tr>
|
|
175
|
+
</tbody>
|
|
176
|
+
</table>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class="card">
|
|
181
|
+
<div class="card-header">
|
|
182
|
+
<h2 class="card-title">Live Telemetry & Event Stream (WebSockets)</h2>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="card-body">
|
|
185
|
+
<div class="event-log-container" id="live-event-stream">
|
|
186
|
+
<div class="event-line">
|
|
187
|
+
<span class="event-time">--:--:--</span>
|
|
188
|
+
<span class="event-type">SYSTEM</span>
|
|
189
|
+
<span class="event-msg">Aguardando eventos do broker Redis...</span>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</section>
|
|
195
|
+
|
|
196
|
+
<!-- 2. Workers Tab -->
|
|
197
|
+
<section id="tab-workers" class="tab-pane">
|
|
198
|
+
<div class="card">
|
|
199
|
+
<div class="card-header" style="flex-wrap: wrap; gap: 12px;">
|
|
200
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
201
|
+
<h2 class="card-title">Workers Conectados</h2>
|
|
202
|
+
<span class="badge badge-active" id="workers-count-badge">0 workers</span>
|
|
203
|
+
</div>
|
|
204
|
+
<div style="display: flex; gap: 8px;">
|
|
205
|
+
<button class="btn btn-secondary btn-sm" onclick="fetchWorkers()">🔄 Atualizar</button>
|
|
206
|
+
<button class="btn btn-primary btn-sm" onclick="openSpawnWorkerModal()">+ Iniciar Novo Worker</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="card-body">
|
|
210
|
+
<div class="workers-grid" id="workers-container">
|
|
211
|
+
<div style="color: var(--ink-subtle);">Nenhum worker ativo encontrado.</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</section>
|
|
216
|
+
|
|
217
|
+
<!-- 3. Queues & Jobs Tab -->
|
|
218
|
+
<section id="tab-queues" class="tab-pane">
|
|
219
|
+
<div class="card">
|
|
220
|
+
<div class="card-header">
|
|
221
|
+
<h2 class="card-title">Explorador de Tarefas Registradas</h2>
|
|
222
|
+
<div style="display: flex; gap: 8px;">
|
|
223
|
+
<button class="btn btn-secondary btn-sm" onclick="openCreateQueueModal()">+ Nova Fila</button>
|
|
224
|
+
<button class="btn btn-primary btn-sm" onclick="openEnqueueModal()">+ Nova Tarefa</button>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
<div class="card-body table-container">
|
|
228
|
+
<table>
|
|
229
|
+
<thead>
|
|
230
|
+
<tr>
|
|
231
|
+
<th>Nome da Tarefa</th>
|
|
232
|
+
<th>Fila Padrão</th>
|
|
233
|
+
<th>Max Retries</th>
|
|
234
|
+
<th>Backoff (s)</th>
|
|
235
|
+
<th>Timeout (s)</th>
|
|
236
|
+
<th>Tipo</th>
|
|
237
|
+
<th>Ações</th>
|
|
238
|
+
</tr>
|
|
239
|
+
</thead>
|
|
240
|
+
<tbody id="tasks-table">
|
|
241
|
+
<tr><td colspan="7" style="text-align: center; color: var(--ink-subtle);">Nenhuma tarefa registrada.</td></tr>
|
|
242
|
+
</tbody>
|
|
243
|
+
</table>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
</section>
|
|
247
|
+
|
|
248
|
+
<!-- 4. Schedules / Cron Tab -->
|
|
249
|
+
<section id="tab-schedules" class="tab-pane">
|
|
250
|
+
<div class="card">
|
|
251
|
+
<div class="card-header">
|
|
252
|
+
<h2 class="card-title">Agendamentos & Cron Jobs</h2>
|
|
253
|
+
<button class="btn btn-primary btn-sm" onclick="openScheduleModal()">+ Adicionar Agendamento</button>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="card-body table-container">
|
|
256
|
+
<table>
|
|
257
|
+
<thead>
|
|
258
|
+
<tr>
|
|
259
|
+
<th>Nome</th>
|
|
260
|
+
<th>Tarefa</th>
|
|
261
|
+
<th>Fila</th>
|
|
262
|
+
<th>Expressão / Intervalo</th>
|
|
263
|
+
<th>Status</th>
|
|
264
|
+
<th>Próxima Execução</th>
|
|
265
|
+
<th>Total Runs</th>
|
|
266
|
+
<th>Ações</th>
|
|
267
|
+
</tr>
|
|
268
|
+
</thead>
|
|
269
|
+
<tbody id="schedules-table">
|
|
270
|
+
<tr><td colspan="8" style="text-align: center; color: var(--ink-subtle);">Nenhum cron cadastrado.</td></tr>
|
|
271
|
+
</tbody>
|
|
272
|
+
</table>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</section>
|
|
276
|
+
|
|
277
|
+
<!-- 5. DLQ Tab -->
|
|
278
|
+
<section id="tab-dlq" class="tab-pane">
|
|
279
|
+
<div class="card">
|
|
280
|
+
<div class="card-header" style="flex-wrap: wrap; gap: 12px;">
|
|
281
|
+
<div style="display: flex; align-items: center; gap: 12px; flex: 1;">
|
|
282
|
+
<h2 class="card-title">Dead Letter Queue (DLQ)</h2>
|
|
283
|
+
<span class="badge badge-failed" id="dlq-count-badge">0 falhas</span>
|
|
284
|
+
</div>
|
|
285
|
+
<div style="display: flex; gap: 8px; align-items: center;">
|
|
286
|
+
<select class="form-select" id="dlq-filter-queue" onchange="fetchDlq()" style="width: 160px; padding: 4px 8px; font-size: 12px;">
|
|
287
|
+
<option value="all">Todas as Filas</option>
|
|
288
|
+
</select>
|
|
289
|
+
<button class="btn btn-danger btn-sm" onclick="handlePurgeDlq()">Limpar DLQ</button>
|
|
290
|
+
<button class="btn btn-secondary btn-sm" onclick="fetchDlq()">🔄 Atualizar</button>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
<div class="card-body table-container">
|
|
294
|
+
<table>
|
|
295
|
+
<thead>
|
|
296
|
+
<tr>
|
|
297
|
+
<th>Job ID</th>
|
|
298
|
+
<th>Tarefa</th>
|
|
299
|
+
<th>Fila</th>
|
|
300
|
+
<th>Erro</th>
|
|
301
|
+
<th>Tentativas</th>
|
|
302
|
+
<th>Ações</th>
|
|
303
|
+
</tr>
|
|
304
|
+
</thead>
|
|
305
|
+
<tbody id="dlq-table">
|
|
306
|
+
<tr><td colspan="6" style="text-align: center; color: var(--ink-subtle);">Nenhum job na DLQ.</td></tr>
|
|
307
|
+
</tbody>
|
|
308
|
+
</table>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</section>
|
|
312
|
+
|
|
313
|
+
<!-- 6. LGTM Observability & Execution History Tab -->
|
|
314
|
+
<section id="tab-history" class="tab-pane">
|
|
315
|
+
<!-- Mimir / Prometheus KPI Metrics Bar -->
|
|
316
|
+
<div class="metrics-grid">
|
|
317
|
+
<div class="metric-card">
|
|
318
|
+
<div class="metric-label">Taxa de Sucesso</div>
|
|
319
|
+
<div class="metric-value" id="obs-success-rate">100%</div>
|
|
320
|
+
<div class="metric-sub" id="obs-success-sub">0 falhas recentes</div>
|
|
321
|
+
<div class="metric-progress">
|
|
322
|
+
<div class="metric-progress-fill" id="obs-success-bar" style="width: 100%;"></div>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
<div class="metric-card">
|
|
326
|
+
<div class="metric-label">Duração Média (ms)</div>
|
|
327
|
+
<div class="metric-value" id="obs-avg-duration">0.0 ms</div>
|
|
328
|
+
<div class="metric-sub">Tempo de execução</div>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="metric-card">
|
|
331
|
+
<div class="metric-label">P95 Latência (ms)</div>
|
|
332
|
+
<div class="metric-value" id="obs-p95-duration">0.0 ms</div>
|
|
333
|
+
<div class="metric-sub">95% das execuções abaixo</div>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="metric-card">
|
|
336
|
+
<div class="metric-label">Vazão (Throughput)</div>
|
|
337
|
+
<div class="metric-value" id="obs-throughput">0 / min</div>
|
|
338
|
+
<div class="metric-sub">Execuções no último minuto</div>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<!-- History Filter & Table -->
|
|
343
|
+
<div class="card">
|
|
344
|
+
<div class="card-header" style="flex-wrap: wrap; gap: 12px;">
|
|
345
|
+
<div style="display: flex; align-items: center; gap: 12px; flex: 1;">
|
|
346
|
+
<h2 class="card-title">Histórico de Execuções</h2>
|
|
347
|
+
<span class="badge badge-active" id="history-count-badge">0 registros</span>
|
|
348
|
+
</div>
|
|
349
|
+
<div style="display: flex; gap: 8px; align-items: center;">
|
|
350
|
+
<select class="form-select" id="history-filter-status" onchange="fetchHistory()" style="width: 140px; padding: 4px 8px; font-size: 12px;">
|
|
351
|
+
<option value="">Todos Status</option>
|
|
352
|
+
<option value="completed">Concluídos</option>
|
|
353
|
+
<option value="failed">Falhas / DLQ</option>
|
|
354
|
+
<option value="active">Em Execução</option>
|
|
355
|
+
<option value="retrying">Retentando</option>
|
|
356
|
+
</select>
|
|
357
|
+
<input type="text" class="form-input" id="history-search-task" placeholder="Filtrar por tarefa..." oninput="debounceFetchHistory()" style="width: 160px; padding: 4px 8px; font-size: 12px;" />
|
|
358
|
+
<button class="btn btn-secondary btn-sm" onclick="fetchHistory()">🔄 Atualizar</button>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
<div class="card-body table-container">
|
|
362
|
+
<table>
|
|
363
|
+
<thead>
|
|
364
|
+
<tr>
|
|
365
|
+
<th>Status</th>
|
|
366
|
+
<th>Job ID</th>
|
|
367
|
+
<th>Tarefa</th>
|
|
368
|
+
<th>Fila</th>
|
|
369
|
+
<th>Duração</th>
|
|
370
|
+
<th>Worker</th>
|
|
371
|
+
<th>Iniciado / Concluído</th>
|
|
372
|
+
<th>Ações</th>
|
|
373
|
+
</tr>
|
|
374
|
+
</thead>
|
|
375
|
+
<tbody id="history-table">
|
|
376
|
+
<tr><td colspan="8" style="text-align: center; color: var(--ink-subtle);">Nenhuma execução recente gravada.</td></tr>
|
|
377
|
+
</tbody>
|
|
378
|
+
</table>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</section>
|
|
382
|
+
</main>
|
|
383
|
+
|
|
384
|
+
<!-- Modal Enqueue Task -->
|
|
385
|
+
<div class="modal-backdrop" id="modal-enqueue">
|
|
386
|
+
<div class="modal">
|
|
387
|
+
<div class="modal-header">
|
|
388
|
+
<h3>Enfileirar Nova Tarefa</h3>
|
|
389
|
+
<button class="modal-close" onclick="closeModal('modal-enqueue')">×</button>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="modal-body">
|
|
392
|
+
<form id="form-enqueue" onsubmit="handleEnqueueSubmit(event)">
|
|
393
|
+
<div class="form-group">
|
|
394
|
+
<label class="form-label">Selecionar Tarefa Registrada</label>
|
|
395
|
+
<select class="form-select" id="enq-task-select" onchange="handleTaskSelectChange('enq')">
|
|
396
|
+
<option value="">-- Carregando tarefas... --</option>
|
|
397
|
+
</select>
|
|
398
|
+
</div>
|
|
399
|
+
<div class="form-group" id="group-enq-custom-task" style="display: none;">
|
|
400
|
+
<label class="form-label">Nome da Tarefa Customizada</label>
|
|
401
|
+
<input type="text" class="form-input" id="enq-task-name" placeholder="ex: meupacote.minha_tarefa" />
|
|
402
|
+
</div>
|
|
403
|
+
<div class="form-group">
|
|
404
|
+
<label class="form-label">Fila (Queue)</label>
|
|
405
|
+
<input type="text" class="form-input" id="enq-queue" value="default" required />
|
|
406
|
+
</div>
|
|
407
|
+
<div class="form-group">
|
|
408
|
+
<label class="form-label">Argumentos JSON / Payload da Tarefa</label>
|
|
409
|
+
<textarea class="form-textarea" id="enq-args" placeholder='{"args": ["param1"], "kwargs": {"key": "value"}}'></textarea>
|
|
410
|
+
<div style="font-size: 11px; color: var(--ink-tertiary); margin-top: 4px;" id="enq-args-help">
|
|
411
|
+
Dica: Para system.run_command, use {"command": "python script.py"}
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
<div class="form-group">
|
|
415
|
+
<label class="form-label">Delay (Segundos de espera, opcional)</label>
|
|
416
|
+
<input type="number" class="form-input" id="enq-delay" placeholder="0" min="0" />
|
|
417
|
+
</div>
|
|
418
|
+
<div class="modal-footer">
|
|
419
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-enqueue')">Cancelar</button>
|
|
420
|
+
<button type="submit" class="btn btn-primary">Enfileirar</button>
|
|
421
|
+
</div>
|
|
422
|
+
</form>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
<!-- Modal Create Schedule -->
|
|
428
|
+
<div class="modal-backdrop" id="modal-schedule">
|
|
429
|
+
<div class="modal">
|
|
430
|
+
<div class="modal-header">
|
|
431
|
+
<h3>Cadastrar Cron / Agendamento</h3>
|
|
432
|
+
<button class="modal-close" onclick="closeModal('modal-schedule')">×</button>
|
|
433
|
+
</div>
|
|
434
|
+
<div class="modal-body">
|
|
435
|
+
<form id="form-schedule" onsubmit="handleScheduleSubmit(event)">
|
|
436
|
+
<div class="form-group">
|
|
437
|
+
<label class="form-label">Nome da Rotina</label>
|
|
438
|
+
<input type="text" class="form-input" id="sched-name" placeholder="ex: Backup Diário" required />
|
|
439
|
+
</div>
|
|
440
|
+
<div class="form-group">
|
|
441
|
+
<label class="form-label">Selecionar Tarefa Registrada</label>
|
|
442
|
+
<select class="form-select" id="sched-task-select" onchange="handleTaskSelectChange('sched')">
|
|
443
|
+
<option value="">-- Carregando tarefas... --</option>
|
|
444
|
+
</select>
|
|
445
|
+
</div>
|
|
446
|
+
<div class="form-group" id="group-sched-custom-task" style="display: none;">
|
|
447
|
+
<label class="form-label">Nome da Tarefa Customizada</label>
|
|
448
|
+
<input type="text" class="form-input" id="sched-task" placeholder="ex: meupacote.minha_tarefa" />
|
|
449
|
+
</div>
|
|
450
|
+
<div class="form-group">
|
|
451
|
+
<label class="form-label">Argumentos JSON (Opcional)</label>
|
|
452
|
+
<textarea class="form-textarea" id="sched-args" placeholder='{"args": [], "kwargs": {"command": "python backup.py"}}'></textarea>
|
|
453
|
+
</div>
|
|
454
|
+
<div class="form-group">
|
|
455
|
+
<label class="form-label">Tipo de Agendamento</label>
|
|
456
|
+
<select class="form-select" id="sched-type" onchange="toggleScheduleTypeFields()">
|
|
457
|
+
<option value="cron">Expressão Cron (ex: */5 * * * *)</option>
|
|
458
|
+
<option value="interval">Intervalo em Segundos</option>
|
|
459
|
+
</select>
|
|
460
|
+
</div>
|
|
461
|
+
<div class="form-group" id="group-cron">
|
|
462
|
+
<label class="form-label">Expressão Cron (5 partes: min hora dia mês dia-semana)</label>
|
|
463
|
+
<input type="text" class="form-input" id="sched-cron" value="*/5 * * * *" placeholder="*/5 * * * *" required />
|
|
464
|
+
</div>
|
|
465
|
+
<div class="form-group" id="group-interval" style="display: none;">
|
|
466
|
+
<label class="form-label">Intervalo (Segundos)</label>
|
|
467
|
+
<input type="number" class="form-input" id="sched-interval" value="60" placeholder="60" min="1" />
|
|
468
|
+
</div>
|
|
469
|
+
<div class="form-group">
|
|
470
|
+
<label class="form-label">Fila (Queue)</label>
|
|
471
|
+
<input type="text" class="form-input" id="sched-queue" value="default" />
|
|
472
|
+
</div>
|
|
473
|
+
<div class="modal-footer">
|
|
474
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-schedule')">Cancelar</button>
|
|
475
|
+
<button type="submit" class="btn btn-primary">Salvar Agendamento</button>
|
|
476
|
+
</div>
|
|
477
|
+
</form>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
<!-- Modal Job Detail -->
|
|
483
|
+
<div class="modal-backdrop" id="modal-job-detail">
|
|
484
|
+
<div class="modal" style="max-width: 650px;">
|
|
485
|
+
<div class="modal-header">
|
|
486
|
+
<h3 id="job-detail-title">Detalhes do Job</h3>
|
|
487
|
+
<button class="modal-close" onclick="closeModal('modal-job-detail')">×</button>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="modal-body">
|
|
490
|
+
<pre id="job-detail-content" class="json-viewer">Carregando...</pre>
|
|
491
|
+
</div>
|
|
492
|
+
<div class="modal-footer">
|
|
493
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-job-detail')">Fechar</button>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
|
|
498
|
+
<!-- Modal LGTM Trace & Logs Inspector -->
|
|
499
|
+
<div class="modal-backdrop" id="modal-lgtm-trace">
|
|
500
|
+
<div class="modal" style="max-width: 780px;">
|
|
501
|
+
<div class="modal-header">
|
|
502
|
+
<div>
|
|
503
|
+
<h3 id="lgtm-modal-title">Observabilidade: Job Detail</h3>
|
|
504
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);" id="lgtm-modal-subtitle">Trace & Logs</div>
|
|
505
|
+
</div>
|
|
506
|
+
<button class="modal-close" onclick="closeModal('modal-lgtm-trace')">×</button>
|
|
507
|
+
</div>
|
|
508
|
+
<div class="modal-body">
|
|
509
|
+
<!-- Tempo-style Trace Timeline -->
|
|
510
|
+
<div style="margin-bottom: 20px;">
|
|
511
|
+
<div style="font-size: 11px; text-transform: uppercase; color: var(--ink-subtle); letter-spacing: 0.05em; margin-bottom: 8px;">
|
|
512
|
+
⏱️ Tempo Trace Timeline (Ciclo de Vida)
|
|
513
|
+
</div>
|
|
514
|
+
<div class="trace-timeline" id="lgtm-trace-timeline">
|
|
515
|
+
<!-- Steps rendered dynamically -->
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
|
|
519
|
+
<!-- Loki-style Log Console -->
|
|
520
|
+
<div style="margin-bottom: 20px;">
|
|
521
|
+
<div style="font-size: 11px; text-transform: uppercase; color: var(--ink-subtle); letter-spacing: 0.05em; margin-bottom: 8px;">
|
|
522
|
+
📜 Loki Logs & Eventos Capturados
|
|
523
|
+
</div>
|
|
524
|
+
<div class="loki-log-console" id="lgtm-logs-console">
|
|
525
|
+
<div class="log-entry"><span class="log-msg">[INFO] Aguardando logs...</span></div>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
<!-- Mimir / Payload Inspection -->
|
|
530
|
+
<div>
|
|
531
|
+
<div style="font-size: 11px; text-transform: uppercase; color: var(--ink-subtle); letter-spacing: 0.05em; margin-bottom: 8px;">
|
|
532
|
+
📦 Payload de Entrada & Retorno
|
|
533
|
+
</div>
|
|
534
|
+
<pre class="json-viewer" id="lgtm-payload-viewer"></pre>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="modal-footer">
|
|
538
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-lgtm-trace')">Fechar</button>
|
|
539
|
+
</div>
|
|
540
|
+
</div>
|
|
541
|
+
</div>
|
|
542
|
+
|
|
543
|
+
<!-- Modal Spawn Dynamic Worker -->
|
|
544
|
+
<div class="modal-backdrop" id="modal-spawn-worker">
|
|
545
|
+
<div class="modal">
|
|
546
|
+
<div class="modal-header">
|
|
547
|
+
<div>
|
|
548
|
+
<h3>Iniciar Novo Worker</h3>
|
|
549
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Configuração de Runtime Dinâmico</div>
|
|
550
|
+
</div>
|
|
551
|
+
<button class="modal-close" onclick="closeModal('modal-spawn-worker')">×</button>
|
|
552
|
+
</div>
|
|
553
|
+
<div class="modal-body">
|
|
554
|
+
<form id="form-spawn-worker" onsubmit="handleSpawnWorkerSubmit(event)">
|
|
555
|
+
<div class="form-group">
|
|
556
|
+
<label class="form-label">Nome do Worker (Identificador)</label>
|
|
557
|
+
<input type="text" class="form-input" id="spawn-worker-name" placeholder="ex: worker-emails-02" />
|
|
558
|
+
</div>
|
|
559
|
+
<div class="form-group">
|
|
560
|
+
<label class="form-label">Filas de Escuta (separadas por vírgula)</label>
|
|
561
|
+
<input type="text" class="form-input" id="spawn-worker-queues" value="default" placeholder="default, emails, reports" required />
|
|
562
|
+
<div style="font-size: 11px; color: var(--ink-tertiary); margin-top: 4px;">
|
|
563
|
+
Dica: O worker consumirá na ordem de prioridade definida acima.
|
|
564
|
+
</div>
|
|
565
|
+
</div>
|
|
566
|
+
<div class="form-group">
|
|
567
|
+
<label class="form-label">Concorrência Máxima (Tarefas simultâneas)</label>
|
|
568
|
+
<input type="number" class="form-input" id="spawn-worker-concurrency" value="5" min="1" max="50" required />
|
|
569
|
+
</div>
|
|
570
|
+
<div class="form-group">
|
|
571
|
+
<label class="form-label">Teto de Memória RSS em MB (Opcional - Backpressure)</label>
|
|
572
|
+
<input type="number" class="form-input" id="spawn-worker-max-mem" placeholder="ex: 512" min="10" />
|
|
573
|
+
</div>
|
|
574
|
+
<div class="form-group">
|
|
575
|
+
<label class="form-label">Teto de CPU em % (Opcional - Backpressure)</label>
|
|
576
|
+
<input type="number" class="form-input" id="spawn-worker-max-cpu" placeholder="ex: 85" min="10" max="100" />
|
|
577
|
+
</div>
|
|
578
|
+
<div class="modal-footer">
|
|
579
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-spawn-worker')">Cancelar</button>
|
|
580
|
+
<button type="submit" class="btn btn-primary">🚀 Iniciar Worker</button>
|
|
581
|
+
</div>
|
|
582
|
+
</form>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
<!-- Modal Maintenance / Flush Redis -->
|
|
587
|
+
<div class="modal-backdrop" id="modal-maintenance">
|
|
588
|
+
<div class="modal">
|
|
589
|
+
<div class="modal-header">
|
|
590
|
+
<div>
|
|
591
|
+
<h3>🧹 Limpeza & Manutenção do Redis</h3>
|
|
592
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Gerenciamento de Armazenamento e Filas</div>
|
|
593
|
+
</div>
|
|
594
|
+
<button class="modal-close" onclick="closeModal('modal-maintenance')">×</button>
|
|
595
|
+
</div>
|
|
596
|
+
<div class="modal-body">
|
|
597
|
+
<p style="font-size: 13px; color: var(--ink-secondary); margin-bottom: 16px;">
|
|
598
|
+
Escolha qual área do Redis você deseja limpar. As operações são executadas de forma atômica no broker.
|
|
599
|
+
</p>
|
|
600
|
+
|
|
601
|
+
<div style="display: flex; flex-direction: column; gap: 12px;">
|
|
602
|
+
<div style="padding: 12px; background: var(--bg-surface-elevated); border: 1px solid var(--hairline); border-radius: var(--radius-md); display: flex; justify-content: space-between; align-items: center;">
|
|
603
|
+
<div>
|
|
604
|
+
<strong>🧹 Limpar Apenas Filas & Jobs</strong>
|
|
605
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Zera tarefas pendentes, agendadas e na DLQ. Mantém workers e agendamentos cron.</div>
|
|
606
|
+
</div>
|
|
607
|
+
<button class="btn btn-secondary btn-sm" onclick="executeMaintenanceFlush('queues')">Limpar Filas</button>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div style="padding: 12px; background: var(--bg-surface-elevated); border: 1px solid var(--hairline); border-radius: var(--radius-md); display: flex; justify-content: space-between; align-items: center;">
|
|
611
|
+
<div>
|
|
612
|
+
<strong>📜 Limpar Histórico de Execuções</strong>
|
|
613
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Zera métricas agregadas e registros de traces anteriores.</div>
|
|
614
|
+
</div>
|
|
615
|
+
<button class="btn btn-secondary btn-sm" onclick="executeMaintenanceFlush('history')">Limpar Histórico</button>
|
|
616
|
+
</div>
|
|
617
|
+
|
|
618
|
+
<div style="padding: 12px; background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: var(--radius-md); display: flex; justify-content: space-between; align-items: center;">
|
|
619
|
+
<div>
|
|
620
|
+
<strong style="color: var(--semantic-error);">⚠️ Reset Total (Flush All)</strong>
|
|
621
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Zera todo o banco de dados do TaskManager no Redis.</div>
|
|
622
|
+
</div>
|
|
623
|
+
<button class="btn btn-danger btn-sm" onclick="executeMaintenanceFlush('all')">Reset Total</button>
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
<div class="modal-footer">
|
|
628
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-maintenance')">Fechar</button>
|
|
629
|
+
</div>
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
|
|
633
|
+
<!-- Modal Create Queue -->
|
|
634
|
+
<div class="modal-backdrop" id="modal-create-queue">
|
|
635
|
+
<div class="modal" style="max-width: 440px;">
|
|
636
|
+
<div class="modal-header">
|
|
637
|
+
<div>
|
|
638
|
+
<h3>📦 Criar Nova Fila</h3>
|
|
639
|
+
<div style="font-size: 12px; color: var(--ink-tertiary);">Registro no Broker Redis</div>
|
|
640
|
+
</div>
|
|
641
|
+
<button class="modal-close" onclick="closeModal('modal-create-queue')">×</button>
|
|
642
|
+
</div>
|
|
643
|
+
<div class="modal-body">
|
|
644
|
+
<form id="form-create-queue" onsubmit="handleCreateQueueSubmit(event)">
|
|
645
|
+
<div class="form-group">
|
|
646
|
+
<label class="form-label">Nome da Fila</label>
|
|
647
|
+
<input type="text" class="form-input" id="create-queue-name" placeholder="ex: payments, webhooks, export" required pattern="[a-zA-Z0-9_\-]+" title="Use apenas letras, números, hífen ou underline" autofocus />
|
|
648
|
+
<div style="font-size: 11px; color: var(--ink-tertiary); margin-top: 6px;">
|
|
649
|
+
A fila será registrada no Redis imediatamente e estará disponível para workers, agendamentos e envio de tarefas.
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
<div class="modal-footer" style="margin: 20px -20px -20px -20px;">
|
|
653
|
+
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-create-queue')">Cancelar</button>
|
|
654
|
+
<button type="submit" class="btn btn-primary">Criar Fila</button>
|
|
655
|
+
</div>
|
|
656
|
+
</form>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
</div>
|
|
660
|
+
|
|
661
|
+
<!-- Modal Command Palette (Ctrl+K) -->
|
|
662
|
+
<div class="modal-backdrop" id="modal-command-palette" onclick="if(event.target===this)closeModal('modal-command-palette')">
|
|
663
|
+
<div class="modal cmd-palette-modal">
|
|
664
|
+
<div class="cmd-palette-search">
|
|
665
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
666
|
+
<input type="text" id="cmd-search-input" placeholder="Digite um comando, ação ou busque uma tarefa..." oninput="handleCommandSearch(this.value)" onkeydown="handleCommandKeyDown(event)" autocomplete="off" />
|
|
667
|
+
<kbd class="kbd-badge" style="cursor: pointer;" onclick="closeModal('modal-command-palette')">ESC</kbd>
|
|
668
|
+
</div>
|
|
669
|
+
<div class="cmd-palette-results" id="cmd-palette-results">
|
|
670
|
+
<!-- Populated dynamically by app.js -->
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
</div>
|
|
674
|
+
</div>
|
|
675
|
+
|
|
676
|
+
<!-- Toast Notification Container -->
|
|
677
|
+
<div id="toast-container" class="toast-container"></div>
|
|
678
|
+
|
|
679
|
+
<script src="static/app.js"></script>
|
|
680
|
+
</body>
|
|
681
|
+
</html>
|