paymongo-cli 1.0.0
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/.github/ISSUE_TEMPLATE/bug-report.md +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +30 -0
- package/.github/workflows/ci-cd.yml +88 -0
- package/.github/workflows/ci.yml +46 -0
- package/.github/workflows/release.yml +54 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/paymongo.js +3 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +398 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/dev.d.ts +4 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +353 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/gui.d.ts +4 -0
- package/dist/commands/gui.d.ts.map +1 -0
- package/dist/commands/gui.js +74 -0
- package/dist/commands/gui.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +268 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +287 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/payments.d.ts +4 -0
- package/dist/commands/payments.d.ts.map +1 -0
- package/dist/commands/payments.js +180 -0
- package/dist/commands/payments.js.map +1 -0
- package/dist/commands/team/index.d.ts +4 -0
- package/dist/commands/team/index.d.ts.map +1 -0
- package/dist/commands/team/index.js +155 -0
- package/dist/commands/team/index.js.map +1 -0
- package/dist/commands/trigger.d.ts +4 -0
- package/dist/commands/trigger.d.ts.map +1 -0
- package/dist/commands/trigger.js +312 -0
- package/dist/commands/trigger.js.map +1 -0
- package/dist/commands/webhooks.d.ts +4 -0
- package/dist/commands/webhooks.d.ts.map +1 -0
- package/dist/commands/webhooks.js +357 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/services/analytics/service.d.ts +29 -0
- package/dist/services/analytics/service.d.ts.map +1 -0
- package/dist/services/analytics/service.js +97 -0
- package/dist/services/analytics/service.js.map +1 -0
- package/dist/services/api/client.d.ts +27 -0
- package/dist/services/api/client.d.ts.map +1 -0
- package/dist/services/api/client.js +151 -0
- package/dist/services/api/client.js.map +1 -0
- package/dist/services/config/manager.d.ts +16 -0
- package/dist/services/config/manager.d.ts.map +1 -0
- package/dist/services/config/manager.js +211 -0
- package/dist/services/config/manager.js.map +1 -0
- package/dist/services/github/auth.d.ts +15 -0
- package/dist/services/github/auth.d.ts.map +1 -0
- package/dist/services/github/auth.js +119 -0
- package/dist/services/github/auth.js.map +1 -0
- package/dist/services/github/client.d.ts +54 -0
- package/dist/services/github/client.d.ts.map +1 -0
- package/dist/services/github/client.js +107 -0
- package/dist/services/github/client.js.map +1 -0
- package/dist/services/github/sync.d.ts +26 -0
- package/dist/services/github/sync.d.ts.map +1 -0
- package/dist/services/github/sync.js +200 -0
- package/dist/services/github/sync.js.map +1 -0
- package/dist/services/web/server.d.ts +30 -0
- package/dist/services/web/server.d.ts.map +1 -0
- package/dist/services/web/server.js +159 -0
- package/dist/services/web/server.js.map +1 -0
- package/dist/types/paymongo.d.ts +118 -0
- package/dist/types/paymongo.d.ts.map +1 -0
- package/dist/types/paymongo.js +3 -0
- package/dist/types/paymongo.js.map +1 -0
- package/dist/utils/cache.d.ts +20 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +164 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/constants.d.ts +32 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +52 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/errors.d.ts +33 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +79 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +53 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/spinner.d.ts +17 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +54 -0
- package/dist/utils/spinner.js.map +1 -0
- package/dist/utils/validator.d.ts +10 -0
- package/dist/utils/validator.d.ts.map +1 -0
- package/dist/utils/validator.js +79 -0
- package/dist/utils/validator.js.map +1 -0
- package/package.json +70 -0
- package/web/index.html +688 -0
package/web/index.html
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>PayMongo CLI Dashboard</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<style>
|
|
14
|
+
/* PayMongo Brand Colors */
|
|
15
|
+
:root {
|
|
16
|
+
--pm-green: #349b18;
|
|
17
|
+
--pm-lime: #e2f485;
|
|
18
|
+
--pm-forest: #0c332b;
|
|
19
|
+
--pm-bg: #eff7ec;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Icon definitions */
|
|
23
|
+
.icon {
|
|
24
|
+
display: inline-block;
|
|
25
|
+
width: 16px;
|
|
26
|
+
height: 16px;
|
|
27
|
+
margin-right: 4px;
|
|
28
|
+
vertical-align: middle;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.icon-check {
|
|
32
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23349B18' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20,6 9,17 4,12'%3E%3C/polyline%3E%3C/svg%3E");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.icon-x {
|
|
36
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23dc3545' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
|
|
37
|
+
}
|
|
38
|
+
* {
|
|
39
|
+
margin: 0;
|
|
40
|
+
padding: 0;
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
body {
|
|
45
|
+
font-family:
|
|
46
|
+
'Inter',
|
|
47
|
+
-apple-system,
|
|
48
|
+
BlinkMacSystemFont,
|
|
49
|
+
'Segoe UI',
|
|
50
|
+
Roboto,
|
|
51
|
+
sans-serif;
|
|
52
|
+
background: var(--pm-bg);
|
|
53
|
+
color: var(--pm-forest);
|
|
54
|
+
line-height: 1.6;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.header {
|
|
58
|
+
background: linear-gradient(135deg, var(--pm-green) 0%, var(--pm-forest) 100%);
|
|
59
|
+
color: white;
|
|
60
|
+
padding: 2rem;
|
|
61
|
+
text-align: center;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.header h1 {
|
|
65
|
+
font-size: 2.5rem;
|
|
66
|
+
margin-bottom: 0.5rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.header p {
|
|
70
|
+
opacity: 0.9;
|
|
71
|
+
font-size: 1.1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.container {
|
|
75
|
+
max-width: 1200px;
|
|
76
|
+
margin: 0 auto;
|
|
77
|
+
padding: 2rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.analytics-grid {
|
|
81
|
+
display: grid;
|
|
82
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
83
|
+
gap: 1rem;
|
|
84
|
+
margin-bottom: 2rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.analytics-card {
|
|
88
|
+
background: white;
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
91
|
+
padding: 1.5rem;
|
|
92
|
+
text-align: center;
|
|
93
|
+
border: 2px solid #f8f9fa;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.analytics-value {
|
|
97
|
+
font-size: 2rem;
|
|
98
|
+
font-weight: bold;
|
|
99
|
+
color: var(--pm-forest);
|
|
100
|
+
margin-bottom: 0.5rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.analytics-label {
|
|
104
|
+
font-size: 0.9rem;
|
|
105
|
+
color: var(--pm-green);
|
|
106
|
+
text-transform: uppercase;
|
|
107
|
+
letter-spacing: 0.5px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.analytics-section {
|
|
111
|
+
background: white;
|
|
112
|
+
border-radius: 8px;
|
|
113
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
114
|
+
padding: 1.5rem;
|
|
115
|
+
margin-bottom: 1rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.analytics-section h3 {
|
|
119
|
+
margin: 0 0 1rem 0;
|
|
120
|
+
color: var(--pm-forest);
|
|
121
|
+
font-size: 1.2rem;
|
|
122
|
+
border-bottom: 2px solid var(--pm-lime);
|
|
123
|
+
padding-bottom: 0.5rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.analytics-events,
|
|
127
|
+
.analytics-errors {
|
|
128
|
+
display: grid;
|
|
129
|
+
gap: 0.5rem;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.event-type-item,
|
|
133
|
+
.error-type-item {
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
align-items: center;
|
|
137
|
+
padding: 0.75rem;
|
|
138
|
+
background: var(--pm-bg);
|
|
139
|
+
border-radius: 4px;
|
|
140
|
+
border-left: 3px solid var(--pm-green);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.error-type-item {
|
|
144
|
+
border-left-color: #dc3545;
|
|
145
|
+
background: #f8d7da;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.event-type-name,
|
|
149
|
+
.error-type-name {
|
|
150
|
+
font-weight: 500;
|
|
151
|
+
color: var(--pm-forest);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.event-type-count,
|
|
155
|
+
.error-type-count {
|
|
156
|
+
background: var(--pm-green);
|
|
157
|
+
color: white;
|
|
158
|
+
padding: 0.25rem 0.5rem;
|
|
159
|
+
border-radius: 12px;
|
|
160
|
+
font-size: 0.8rem;
|
|
161
|
+
font-weight: bold;
|
|
162
|
+
min-width: 24px;
|
|
163
|
+
text-align: center;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.error-type-count {
|
|
167
|
+
background: #dc3545;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.grid {
|
|
171
|
+
display: grid;
|
|
172
|
+
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
173
|
+
gap: 2rem;
|
|
174
|
+
margin-top: 2rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.card {
|
|
178
|
+
background: white;
|
|
179
|
+
border-radius: 8px;
|
|
180
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.card-header {
|
|
185
|
+
background: #f8f9fa;
|
|
186
|
+
padding: 1rem 1.5rem;
|
|
187
|
+
border-bottom: 1px solid #e9ecef;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.card-header h2 {
|
|
191
|
+
font-size: 1.2rem;
|
|
192
|
+
color: var(--pm-forest);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.card-body {
|
|
196
|
+
padding: 1.5rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.status-indicator {
|
|
200
|
+
display: inline-block;
|
|
201
|
+
width: 8px;
|
|
202
|
+
height: 8px;
|
|
203
|
+
border-radius: 50%;
|
|
204
|
+
margin-right: 0.5rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.status-online {
|
|
208
|
+
background: var(--pm-green);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.status-offline {
|
|
212
|
+
background: #dc3545;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.config-item {
|
|
216
|
+
display: flex;
|
|
217
|
+
justify-content: space-between;
|
|
218
|
+
align-items: center;
|
|
219
|
+
padding: 0.75rem 0;
|
|
220
|
+
border-bottom: 1px solid #f8f9fa;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.config-item:last-child {
|
|
224
|
+
border-bottom: none;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.config-key {
|
|
228
|
+
font-weight: 500;
|
|
229
|
+
color: var(--pm-forest);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.config-value {
|
|
233
|
+
color: var(--pm-green);
|
|
234
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
235
|
+
font-size: 0.9rem;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.webhook-list {
|
|
239
|
+
max-height: 300px;
|
|
240
|
+
overflow-y: auto;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.webhook-item {
|
|
244
|
+
padding: 1rem;
|
|
245
|
+
border: 1px solid #e9ecef;
|
|
246
|
+
border-radius: 4px;
|
|
247
|
+
margin-bottom: 0.5rem;
|
|
248
|
+
background: #f8f9fa;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.webhook-url {
|
|
252
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
253
|
+
font-size: 0.9rem;
|
|
254
|
+
color: var(--pm-green);
|
|
255
|
+
word-break: break-all;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.webhook-events {
|
|
259
|
+
font-size: 0.9rem;
|
|
260
|
+
color: var(--pm-forest);
|
|
261
|
+
margin-top: 0.25rem;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.webhook-status {
|
|
265
|
+
display: inline-block;
|
|
266
|
+
padding: 0.25rem 0.5rem;
|
|
267
|
+
border-radius: 4px;
|
|
268
|
+
font-size: 0.8rem;
|
|
269
|
+
font-weight: 500;
|
|
270
|
+
text-transform: uppercase;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.status-enabled {
|
|
274
|
+
background: #d4edda;
|
|
275
|
+
color: #155724;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.status-disabled {
|
|
279
|
+
background: #f8d7da;
|
|
280
|
+
color: #721c24;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.events-log {
|
|
284
|
+
max-height: 400px;
|
|
285
|
+
overflow-y: auto;
|
|
286
|
+
background: #f8f9fa;
|
|
287
|
+
border: 1px solid #e9ecef;
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
padding: 1rem;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.event-item {
|
|
293
|
+
padding: 0.5rem;
|
|
294
|
+
margin-bottom: 0.5rem;
|
|
295
|
+
border-left: 3px solid var(--pm-green);
|
|
296
|
+
background: var(--pm-bg);
|
|
297
|
+
border-radius: 4px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.event-type {
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
color: var(--pm-green);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.event-time {
|
|
306
|
+
font-size: 0.8rem;
|
|
307
|
+
color: var(--pm-forest);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.event-data {
|
|
311
|
+
margin-top: 0.5rem;
|
|
312
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
313
|
+
font-size: 0.8rem;
|
|
314
|
+
background: #f8f9fa;
|
|
315
|
+
padding: 0.5rem;
|
|
316
|
+
border-radius: 4px;
|
|
317
|
+
overflow-x: auto;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.loading {
|
|
321
|
+
text-align: center;
|
|
322
|
+
padding: 2rem;
|
|
323
|
+
color: var(--pm-forest);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.error {
|
|
327
|
+
background: #f8d7da;
|
|
328
|
+
color: #721c24;
|
|
329
|
+
padding: 1rem;
|
|
330
|
+
border-radius: 4px;
|
|
331
|
+
margin: 1rem 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@media (max-width: 768px) {
|
|
335
|
+
.grid {
|
|
336
|
+
grid-template-columns: 1fr;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.header {
|
|
340
|
+
padding: 1rem;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.header h1 {
|
|
344
|
+
font-size: 2rem;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
</style>
|
|
348
|
+
</head>
|
|
349
|
+
<body>
|
|
350
|
+
<header class="header">
|
|
351
|
+
<h1>PayMongo CLI Dashboard</h1>
|
|
352
|
+
<p>Real-time webhook monitoring and configuration management</p>
|
|
353
|
+
</header>
|
|
354
|
+
|
|
355
|
+
<div class="container">
|
|
356
|
+
<div id="error-message" class="error" style="display: none"></div>
|
|
357
|
+
|
|
358
|
+
<div class="grid">
|
|
359
|
+
<div class="card">
|
|
360
|
+
<div class="card-header">
|
|
361
|
+
<h2>
|
|
362
|
+
<span class="status-indicator status-offline" id="connection-status"></span>System
|
|
363
|
+
Status
|
|
364
|
+
</h2>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="card-body">
|
|
367
|
+
<div id="system-status" class="loading">Loading system status...</div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<div class="card">
|
|
372
|
+
<div class="card-header">
|
|
373
|
+
<h2>Configuration</h2>
|
|
374
|
+
</div>
|
|
375
|
+
<div class="card-body">
|
|
376
|
+
<div id="config-list" class="loading">Loading configuration...</div>
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div class="card">
|
|
381
|
+
<div class="card-header">
|
|
382
|
+
<h2>Webhooks</h2>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="card-body">
|
|
385
|
+
<div id="webhooks-list" class="loading">Loading webhooks...</div>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div class="card">
|
|
390
|
+
<div class="card-header">
|
|
391
|
+
<h2>Analytics</h2>
|
|
392
|
+
</div>
|
|
393
|
+
<div class="card-body">
|
|
394
|
+
<div id="analytics-data" class="loading">Loading analytics...</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
|
|
398
|
+
<div class="card">
|
|
399
|
+
<div class="card-header">
|
|
400
|
+
<h2>Recent Events</h2>
|
|
401
|
+
</div>
|
|
402
|
+
<div class="card-body">
|
|
403
|
+
<div id="events-log" class="events-log">
|
|
404
|
+
<div class="loading">Waiting for webhook events...</div>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<script>
|
|
412
|
+
class Dashboard {
|
|
413
|
+
constructor() {
|
|
414
|
+
this.socket = null;
|
|
415
|
+
this.init();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
init() {
|
|
419
|
+
this.connectWebSocket();
|
|
420
|
+
this.loadSystemStatus();
|
|
421
|
+
this.loadConfiguration();
|
|
422
|
+
this.loadWebhooks();
|
|
423
|
+
this.loadAnalytics();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
connectWebSocket() {
|
|
427
|
+
this.socket = io();
|
|
428
|
+
|
|
429
|
+
this.socket.on('connect', () => {
|
|
430
|
+
this.updateConnectionStatus(true);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
this.socket.on('disconnect', () => {
|
|
434
|
+
this.updateConnectionStatus(false);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
this.socket.on('webhook:event', (event) => {
|
|
438
|
+
this.addWebhookEvent(event);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
this.socket.on('config:updated', (data) => {
|
|
442
|
+
this.showNotification(`Configuration updated: ${data.key} = ${data.value}`);
|
|
443
|
+
this.loadConfiguration();
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
updateConnectionStatus(connected) {
|
|
448
|
+
const indicator = document.getElementById('connection-status');
|
|
449
|
+
const status = document.getElementById('system-status');
|
|
450
|
+
|
|
451
|
+
if (connected) {
|
|
452
|
+
indicator.className = 'status-indicator status-online';
|
|
453
|
+
status.innerHTML =
|
|
454
|
+
'<span class="icon icon-check"></span><strong>Connected</strong><br>Real-time updates active';
|
|
455
|
+
} else {
|
|
456
|
+
indicator.className = 'status-indicator status-offline';
|
|
457
|
+
status.innerHTML =
|
|
458
|
+
'<span class="icon icon-x"></span><strong>Disconnected</strong><br>Real-time updates disabled';
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async loadSystemStatus() {
|
|
463
|
+
try {
|
|
464
|
+
const response = await fetch('/api/config');
|
|
465
|
+
const data = await response.json();
|
|
466
|
+
|
|
467
|
+
if (data.success) {
|
|
468
|
+
const config = data.data;
|
|
469
|
+
const status = document.getElementById('system-status');
|
|
470
|
+
status.innerHTML = `
|
|
471
|
+
<div class="config-item">
|
|
472
|
+
<span class="config-key">Project:</span>
|
|
473
|
+
<span class="config-value">${config.projectName || 'Not set'}</span>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="config-item">
|
|
476
|
+
<span class="config-key">Environment:</span>
|
|
477
|
+
<span class="config-value">${config.environment || 'Not set'}</span>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="config-item">
|
|
480
|
+
<span class="config-key">Webhook URL:</span>
|
|
481
|
+
<span class="config-value">${config.webhooks?.url || 'Not set'}</span>
|
|
482
|
+
</div>
|
|
483
|
+
`;
|
|
484
|
+
}
|
|
485
|
+
} catch (error) {
|
|
486
|
+
this.showError('Failed to load system status: ' + error.message);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async loadConfiguration() {
|
|
491
|
+
try {
|
|
492
|
+
const response = await fetch('/api/config');
|
|
493
|
+
const data = await response.json();
|
|
494
|
+
|
|
495
|
+
if (data.success) {
|
|
496
|
+
const config = data.data;
|
|
497
|
+
const configList = document.getElementById('config-list');
|
|
498
|
+
configList.innerHTML = '';
|
|
499
|
+
|
|
500
|
+
// Flatten nested config for display
|
|
501
|
+
const flattenConfig = (obj, prefix = '') => {
|
|
502
|
+
const items = [];
|
|
503
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
504
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
505
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
506
|
+
items.push(...flattenConfig(value, fullKey));
|
|
507
|
+
} else {
|
|
508
|
+
items.push({
|
|
509
|
+
key: fullKey,
|
|
510
|
+
value: Array.isArray(value) ? value.join(', ') : String(value),
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return items;
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const configItems = flattenConfig(config);
|
|
518
|
+
configItems.forEach((item) => {
|
|
519
|
+
const itemDiv = document.createElement('div');
|
|
520
|
+
itemDiv.className = 'config-item';
|
|
521
|
+
itemDiv.innerHTML = `
|
|
522
|
+
<span class="config-key">${item.key}:</span>
|
|
523
|
+
<span class="config-value">${item.value}</span>
|
|
524
|
+
`;
|
|
525
|
+
configList.appendChild(itemDiv);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
this.showError('Failed to load configuration: ' + error.message);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async loadWebhooks() {
|
|
534
|
+
try {
|
|
535
|
+
const response = await fetch('/api/webhooks');
|
|
536
|
+
const data = await response.json();
|
|
537
|
+
|
|
538
|
+
if (data.success) {
|
|
539
|
+
const webhooks = data.data;
|
|
540
|
+
const webhooksList = document.getElementById('webhooks-list');
|
|
541
|
+
|
|
542
|
+
if (webhooks.length === 0) {
|
|
543
|
+
webhooksList.innerHTML = '<p>No webhooks configured</p>';
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
webhooksList.innerHTML = '<div class="webhook-list">';
|
|
548
|
+
webhooks.forEach((webhook) => {
|
|
549
|
+
const webhookDiv = document.createElement('div');
|
|
550
|
+
webhookDiv.className = 'webhook-item';
|
|
551
|
+
webhookDiv.innerHTML = `
|
|
552
|
+
<div class="webhook-url">${webhook.attributes.url}</div>
|
|
553
|
+
<div class="webhook-events">Events: ${webhook.attributes.events.join(', ')}</div>
|
|
554
|
+
<span class="webhook-status status-${webhook.attributes.status}">${webhook.attributes.status}</span>
|
|
555
|
+
`;
|
|
556
|
+
webhooksList.appendChild(webhookDiv);
|
|
557
|
+
});
|
|
558
|
+
webhooksList.innerHTML += '</div>';
|
|
559
|
+
}
|
|
560
|
+
} catch (error) {
|
|
561
|
+
this.showError('Failed to load webhooks: ' + error.message);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async loadAnalytics() {
|
|
566
|
+
try {
|
|
567
|
+
const response = await fetch('/api/analytics');
|
|
568
|
+
const data = await response.json();
|
|
569
|
+
|
|
570
|
+
if (data.success) {
|
|
571
|
+
const analytics = data.data;
|
|
572
|
+
const analyticsDiv = document.getElementById('analytics-data');
|
|
573
|
+
analyticsDiv.innerHTML = this.renderAnalytics(analytics);
|
|
574
|
+
}
|
|
575
|
+
} catch (error) {
|
|
576
|
+
this.showError('Failed to load analytics: ' + error.message);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
renderAnalytics(analytics) {
|
|
581
|
+
const { totalEvents, successRate, eventsByType, averageResponseTime, errorsByType } =
|
|
582
|
+
analytics;
|
|
583
|
+
|
|
584
|
+
return `
|
|
585
|
+
<div class="analytics-grid">
|
|
586
|
+
<div class="analytics-card">
|
|
587
|
+
<div class="analytics-value">${totalEvents}</div>
|
|
588
|
+
<div class="analytics-label">Total Events</div>
|
|
589
|
+
</div>
|
|
590
|
+
<div class="analytics-card">
|
|
591
|
+
<div class="analytics-value">${successRate}%</div>
|
|
592
|
+
<div class="analytics-label">Success Rate</div>
|
|
593
|
+
</div>
|
|
594
|
+
<div class="analytics-card">
|
|
595
|
+
<div class="analytics-value">${averageResponseTime}ms</div>
|
|
596
|
+
<div class="analytics-label">Avg Response Time</div>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
|
|
600
|
+
<div class="analytics-section">
|
|
601
|
+
<h3>Events by Type</h3>
|
|
602
|
+
<div class="analytics-events">
|
|
603
|
+
${Object.entries(eventsByType)
|
|
604
|
+
.map(
|
|
605
|
+
([type, count]) => `
|
|
606
|
+
<div class="event-type-item">
|
|
607
|
+
<span class="event-type-name">${type}</span>
|
|
608
|
+
<span class="event-type-count">${count}</span>
|
|
609
|
+
</div>
|
|
610
|
+
`
|
|
611
|
+
)
|
|
612
|
+
.join('')}
|
|
613
|
+
</div>
|
|
614
|
+
</div>
|
|
615
|
+
|
|
616
|
+
${
|
|
617
|
+
Object.keys(errorsByType).length > 0
|
|
618
|
+
? `
|
|
619
|
+
<div class="analytics-section">
|
|
620
|
+
<h3>Errors by Type</h3>
|
|
621
|
+
<div class="analytics-errors">
|
|
622
|
+
${Object.entries(errorsByType)
|
|
623
|
+
.map(
|
|
624
|
+
([type, count]) => `
|
|
625
|
+
<div class="error-type-item">
|
|
626
|
+
<span class="error-type-name">${type}</span>
|
|
627
|
+
<span class="error-type-count">${count}</span>
|
|
628
|
+
</div>
|
|
629
|
+
`
|
|
630
|
+
)
|
|
631
|
+
.join('')}
|
|
632
|
+
</div>
|
|
633
|
+
</div>
|
|
634
|
+
`
|
|
635
|
+
: ''
|
|
636
|
+
}
|
|
637
|
+
`;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
addWebhookEvent(event) {
|
|
641
|
+
const eventsLog = document.getElementById('events-log');
|
|
642
|
+
const eventDiv = document.createElement('div');
|
|
643
|
+
eventDiv.className = 'event-item';
|
|
644
|
+
|
|
645
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
646
|
+
eventDiv.innerHTML = `
|
|
647
|
+
<div class="event-type">${event.type || 'Unknown Event'}</div>
|
|
648
|
+
<div class="event-time">${timestamp}</div>
|
|
649
|
+
<div class="event-data">${JSON.stringify(event.data || event, null, 2)}</div>
|
|
650
|
+
`;
|
|
651
|
+
|
|
652
|
+
// Remove loading message if present
|
|
653
|
+
const loading = eventsLog.querySelector('.loading');
|
|
654
|
+
if (loading) {
|
|
655
|
+
loading.remove();
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
eventsLog.insertBefore(eventDiv, eventsLog.firstChild);
|
|
659
|
+
|
|
660
|
+
// Keep only last 50 events
|
|
661
|
+
while (eventsLog.children.length > 50) {
|
|
662
|
+
eventsLog.removeChild(eventsLog.lastChild);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
showError(message) {
|
|
667
|
+
const errorDiv = document.getElementById('error-message');
|
|
668
|
+
errorDiv.textContent = message;
|
|
669
|
+
errorDiv.style.display = 'block';
|
|
670
|
+
setTimeout(() => {
|
|
671
|
+
errorDiv.style.display = 'none';
|
|
672
|
+
}, 5000);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
showNotification(message) {
|
|
676
|
+
// Simple notification - could be enhanced
|
|
677
|
+
console.log('Notification:', message);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Initialize dashboard when page loads
|
|
682
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
683
|
+
new Dashboard();
|
|
684
|
+
});
|
|
685
|
+
</script>
|
|
686
|
+
<script src="/socket.io/socket.io.js"></script>
|
|
687
|
+
</body>
|
|
688
|
+
</html>
|