@deployforme/core 1.0.1 → 1.0.3
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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/kernel.d.ts +9 -1
- package/dist/kernel.js +63 -24
- package/dist/monitoring/dashboard.d.ts +14 -0
- package/dist/monitoring/dashboard.js +386 -0
- package/dist/monitoring/index.d.ts +3 -0
- package/dist/monitoring/index.js +22 -0
- package/dist/monitoring/monitor.d.ts +21 -0
- package/dist/monitoring/monitor.js +75 -0
- package/dist/monitoring/types.d.ts +28 -0
- package/dist/monitoring/types.js +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -25,3 +25,4 @@ var context_1 = require("./context");
|
|
|
25
25
|
Object.defineProperty(exports, "createRuntimeContext", { enumerable: true, get: function () { return context_1.createRuntimeContext; } });
|
|
26
26
|
Object.defineProperty(exports, "DefaultLogger", { enumerable: true, get: function () { return context_1.DefaultLogger; } });
|
|
27
27
|
__exportStar(require("./types"), exports);
|
|
28
|
+
__exportStar(require("./monitoring"), exports);
|
package/dist/kernel.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { RuntimeContext } from './types';
|
|
2
|
+
import { Monitor } from './monitoring/monitor';
|
|
3
|
+
import { MonitoringConfig } from './monitoring/types';
|
|
2
4
|
export declare class Kernel {
|
|
3
5
|
private registry;
|
|
4
6
|
private loader;
|
|
5
7
|
private context;
|
|
6
8
|
private routeTracker;
|
|
7
|
-
|
|
9
|
+
private monitor;
|
|
10
|
+
private dashboard?;
|
|
11
|
+
private config;
|
|
12
|
+
constructor(context: RuntimeContext, config?: MonitoringConfig);
|
|
13
|
+
private startDashboard;
|
|
14
|
+
stopDashboard(): void;
|
|
15
|
+
getMonitor(): Monitor;
|
|
8
16
|
load(modulePath: string): Promise<void>;
|
|
9
17
|
unload(moduleName: string): Promise<void>;
|
|
10
18
|
reload(modulePath: string): Promise<void>;
|
package/dist/kernel.js
CHANGED
|
@@ -3,43 +3,80 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Kernel = void 0;
|
|
4
4
|
const module_registry_1 = require("./module-registry");
|
|
5
5
|
const loader_1 = require("./loader");
|
|
6
|
+
const monitor_1 = require("./monitoring/monitor");
|
|
7
|
+
const dashboard_1 = require("./monitoring/dashboard");
|
|
6
8
|
class Kernel {
|
|
7
|
-
constructor(context) {
|
|
9
|
+
constructor(context, config = { liveRunnerActions: false }) {
|
|
8
10
|
this.routeTracker = new Map();
|
|
9
11
|
this.context = context;
|
|
10
12
|
this.registry = new module_registry_1.ModuleRegistry();
|
|
11
13
|
this.loader = new loader_1.ModuleLoader();
|
|
14
|
+
this.monitor = new monitor_1.Monitor();
|
|
15
|
+
this.config = config;
|
|
16
|
+
if (config.liveRunnerActions) {
|
|
17
|
+
this.startDashboard(config.port, config.host);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async startDashboard(port, host) {
|
|
21
|
+
try {
|
|
22
|
+
this.dashboard = new dashboard_1.Dashboard(this.monitor, port || 0, host || 'localhost');
|
|
23
|
+
await this.dashboard.start();
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
this.context.logger?.error(`[Deploy4Me] Failed to start dashboard: ${error}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
stopDashboard() {
|
|
30
|
+
if (this.dashboard) {
|
|
31
|
+
this.dashboard.stop();
|
|
32
|
+
this.dashboard = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
getMonitor() {
|
|
36
|
+
return this.monitor;
|
|
12
37
|
}
|
|
13
38
|
async load(modulePath) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
39
|
+
const buildId = this.monitor.startBuild('unknown', modulePath);
|
|
40
|
+
try {
|
|
41
|
+
const module = await this.loader.load(modulePath);
|
|
42
|
+
this.monitor.completeBuild(buildId, 'success');
|
|
43
|
+
this.monitor.startBuild(module.name, modulePath);
|
|
44
|
+
// Unload existing module if present
|
|
45
|
+
if (this.registry.has(module.name)) {
|
|
46
|
+
await this.unload(module.name);
|
|
47
|
+
}
|
|
48
|
+
this.context.logger?.log(`[Deploy4Me] Loading module: ${module.name}@${module.version}`);
|
|
49
|
+
// Track routes registered during module.register()
|
|
50
|
+
const routeIds = [];
|
|
51
|
+
const originalRegister = this.context.http.registerRoute.bind(this.context.http);
|
|
52
|
+
this.context.http.registerRoute = (definition) => {
|
|
53
|
+
routeIds.push(definition.id);
|
|
54
|
+
originalRegister(definition);
|
|
55
|
+
};
|
|
56
|
+
// Register module
|
|
57
|
+
module.register(this.context);
|
|
58
|
+
// Restore original registerRoute
|
|
59
|
+
this.context.http.registerRoute = originalRegister;
|
|
60
|
+
// Store in registry
|
|
61
|
+
this.registry.register(module, routeIds);
|
|
62
|
+
this.routeTracker.set(module.name, routeIds);
|
|
63
|
+
// Register in monitor
|
|
64
|
+
this.monitor.registerModule(module.name, module.version, routeIds.length);
|
|
65
|
+
this.context.logger?.log(`[Deploy4Me] Module registered: ${module.name} (${routeIds.length} routes)`);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
this.monitor.completeBuild(buildId, 'error', errorMessage);
|
|
70
|
+
throw error;
|
|
18
71
|
}
|
|
19
|
-
this.context.logger?.log(`Loading module: ${module.name}@${module.version}`);
|
|
20
|
-
// Track routes registered during module.register()
|
|
21
|
-
const routeIds = [];
|
|
22
|
-
const originalRegister = this.context.http.registerRoute.bind(this.context.http);
|
|
23
|
-
this.context.http.registerRoute = (definition) => {
|
|
24
|
-
routeIds.push(definition.id);
|
|
25
|
-
originalRegister(definition);
|
|
26
|
-
};
|
|
27
|
-
// Register module
|
|
28
|
-
module.register(this.context);
|
|
29
|
-
// Restore original registerRoute
|
|
30
|
-
this.context.http.registerRoute = originalRegister;
|
|
31
|
-
// Store in registry
|
|
32
|
-
this.registry.register(module, routeIds);
|
|
33
|
-
this.routeTracker.set(module.name, routeIds);
|
|
34
|
-
this.context.logger?.log(`Module loaded: ${module.name} (${routeIds.length} routes)`);
|
|
35
72
|
}
|
|
36
73
|
async unload(moduleName) {
|
|
37
74
|
const metadata = this.registry.get(moduleName);
|
|
38
75
|
if (!metadata) {
|
|
39
|
-
this.context.logger?.warn(`Module not found: ${moduleName}`);
|
|
76
|
+
this.context.logger?.warn(`[Deploy4Me] Module not found: ${moduleName}`);
|
|
40
77
|
return;
|
|
41
78
|
}
|
|
42
|
-
this.context.logger?.log(`Unloading module: ${moduleName}`);
|
|
79
|
+
this.context.logger?.log(`[Deploy4Me] Unloading module: ${moduleName}`);
|
|
43
80
|
// Call dispose if exists
|
|
44
81
|
if (metadata.module.dispose) {
|
|
45
82
|
metadata.module.dispose();
|
|
@@ -52,7 +89,9 @@ class Kernel {
|
|
|
52
89
|
// Remove from registry
|
|
53
90
|
this.registry.unregister(moduleName);
|
|
54
91
|
this.routeTracker.delete(moduleName);
|
|
55
|
-
|
|
92
|
+
// Unregister from monitor
|
|
93
|
+
this.monitor.unregisterModule(moduleName);
|
|
94
|
+
this.context.logger?.log(`[Deploy4Me] Module unloaded: ${moduleName}`);
|
|
56
95
|
}
|
|
57
96
|
async reload(modulePath) {
|
|
58
97
|
await this.load(modulePath);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Monitor } from './monitor';
|
|
2
|
+
export declare class Dashboard {
|
|
3
|
+
private server?;
|
|
4
|
+
private monitor;
|
|
5
|
+
private port;
|
|
6
|
+
private host;
|
|
7
|
+
constructor(monitor: Monitor, port?: number, host?: string);
|
|
8
|
+
start(): Promise<number>;
|
|
9
|
+
stop(): void;
|
|
10
|
+
private handleRequest;
|
|
11
|
+
private sendJSON;
|
|
12
|
+
private sendHTML;
|
|
13
|
+
private getHTML;
|
|
14
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Dashboard = void 0;
|
|
37
|
+
const http = __importStar(require("http"));
|
|
38
|
+
class Dashboard {
|
|
39
|
+
constructor(monitor, port = 0, host = 'localhost') {
|
|
40
|
+
this.monitor = monitor;
|
|
41
|
+
this.port = port;
|
|
42
|
+
this.host = host;
|
|
43
|
+
}
|
|
44
|
+
async start() {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
this.server = http.createServer((req, res) => {
|
|
47
|
+
this.handleRequest(req, res);
|
|
48
|
+
});
|
|
49
|
+
this.server.on('error', reject);
|
|
50
|
+
this.server.listen(this.port, this.host, () => {
|
|
51
|
+
const addr = this.server.address();
|
|
52
|
+
const actualPort = typeof addr === 'object' ? addr.port : this.port;
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
console.log(`[${timestamp}] [SUCCESS] Dashboard started at http://${this.host}:${actualPort}`);
|
|
55
|
+
resolve(actualPort);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
stop() {
|
|
60
|
+
if (this.server) {
|
|
61
|
+
this.server.close();
|
|
62
|
+
this.server = undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
handleRequest(req, res) {
|
|
66
|
+
const url = req.url || '/';
|
|
67
|
+
if (url === '/api/state') {
|
|
68
|
+
this.sendJSON(res, {
|
|
69
|
+
builds: this.monitor.getBuilds(),
|
|
70
|
+
modules: this.monitor.getActiveModules(),
|
|
71
|
+
stats: this.monitor.getStats()
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.sendHTML(res);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
sendJSON(res, data) {
|
|
79
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
80
|
+
res.end(JSON.stringify(data));
|
|
81
|
+
}
|
|
82
|
+
sendHTML(res) {
|
|
83
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
84
|
+
res.end(this.getHTML());
|
|
85
|
+
}
|
|
86
|
+
getHTML() {
|
|
87
|
+
return `<!DOCTYPE html>
|
|
88
|
+
<html lang="tr">
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="UTF-8">
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
92
|
+
<title>Deploy4Me Dashboard</title>
|
|
93
|
+
<style>
|
|
94
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
|
|
95
|
+
|
|
96
|
+
:root {
|
|
97
|
+
--bg: #0a0a0a;
|
|
98
|
+
--text: #f2f2f2;
|
|
99
|
+
--text-dim: #666666;
|
|
100
|
+
--border: #1a1a1a;
|
|
101
|
+
--success: #4ade80;
|
|
102
|
+
--error: #f87171;
|
|
103
|
+
--process: #60a5fa;
|
|
104
|
+
--accent: #ffffff;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
108
|
+
|
|
109
|
+
body {
|
|
110
|
+
font-family: 'Inter', sans-serif;
|
|
111
|
+
background: var(--bg);
|
|
112
|
+
color: var(--text);
|
|
113
|
+
line-height: 1.6;
|
|
114
|
+
padding: 4rem 2rem;
|
|
115
|
+
display: flex;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
-webkit-font-smoothing: antialiased;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.container { width: 100%; max-width: 900px; }
|
|
121
|
+
|
|
122
|
+
header {
|
|
123
|
+
margin-bottom: 4rem;
|
|
124
|
+
border-bottom: 1px solid var(--text);
|
|
125
|
+
padding-bottom: 1.5rem;
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: space-between;
|
|
128
|
+
align-items: flex-end;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
h1 { font-size: 1.4rem; font-weight: 500; letter-spacing: -0.03em; text-transform: lowercase; }
|
|
132
|
+
.subtitle { color: var(--text-dim); font-size: 0.8rem; }
|
|
133
|
+
|
|
134
|
+
.stats {
|
|
135
|
+
display: grid;
|
|
136
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
137
|
+
gap: 2rem;
|
|
138
|
+
margin-bottom: 5rem;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.stat-label { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-dim); margin-bottom: 0.5rem; }
|
|
142
|
+
.stat-value { font-size: 1.4rem; font-variant-numeric: tabular-nums; }
|
|
143
|
+
|
|
144
|
+
.section { margin-bottom: 4rem; }
|
|
145
|
+
|
|
146
|
+
.section-header {
|
|
147
|
+
display: flex;
|
|
148
|
+
justify-content: space-between;
|
|
149
|
+
align-items: center;
|
|
150
|
+
border-bottom: 1px solid var(--border);
|
|
151
|
+
margin-bottom: 1rem;
|
|
152
|
+
padding-bottom: 0.5rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.section-title {
|
|
156
|
+
font-size: 0.75rem;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
text-transform: uppercase;
|
|
159
|
+
letter-spacing: 0.15em;
|
|
160
|
+
color: var(--text-dim);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Tabs Styling */
|
|
164
|
+
.tabs { display: flex; gap: 1.5rem; }
|
|
165
|
+
.tab {
|
|
166
|
+
background: none;
|
|
167
|
+
border: none;
|
|
168
|
+
color: var(--text-dim);
|
|
169
|
+
font-family: inherit;
|
|
170
|
+
font-size: 0.75rem;
|
|
171
|
+
font-weight: 600;
|
|
172
|
+
text-transform: uppercase;
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
padding: 0.5rem 0;
|
|
175
|
+
transition: color 0.2s;
|
|
176
|
+
position: relative;
|
|
177
|
+
}
|
|
178
|
+
.tab:hover { color: var(--text); }
|
|
179
|
+
.tab.active { color: var(--accent); }
|
|
180
|
+
.tab.active::after {
|
|
181
|
+
content: '';
|
|
182
|
+
position: absolute;
|
|
183
|
+
bottom: -0.6rem;
|
|
184
|
+
left: 0;
|
|
185
|
+
width: 100%;
|
|
186
|
+
height: 1px;
|
|
187
|
+
background: var(--accent);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.list-item {
|
|
191
|
+
display: grid;
|
|
192
|
+
grid-template-columns: 1fr auto;
|
|
193
|
+
padding: 1rem 0;
|
|
194
|
+
border-bottom: 1px solid var(--border);
|
|
195
|
+
align-items: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.item-name { font-weight: 500; font-size: 1rem; }
|
|
199
|
+
.item-details {
|
|
200
|
+
font-size: 0.75rem;
|
|
201
|
+
color: var(--text-dim);
|
|
202
|
+
display: flex;
|
|
203
|
+
gap: 1rem;
|
|
204
|
+
font-family: 'ui-monospace', monospace;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.status-text { font-size: 0.65rem; font-weight: 600; text-transform: uppercase; }
|
|
208
|
+
.status-building { color: var(--process); }
|
|
209
|
+
.status-success { color: var(--success); }
|
|
210
|
+
.status-error { color: var(--error); }
|
|
211
|
+
|
|
212
|
+
.error-log {
|
|
213
|
+
grid-column: 1 / -1;
|
|
214
|
+
font-family: 'ui-monospace', monospace;
|
|
215
|
+
font-size: 0.7rem;
|
|
216
|
+
color: var(--error);
|
|
217
|
+
padding: 0.5rem 0;
|
|
218
|
+
opacity: 0.8;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Pagination Styling */
|
|
222
|
+
.pagination {
|
|
223
|
+
display: flex;
|
|
224
|
+
justify-content: center;
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: 2rem;
|
|
227
|
+
margin-top: 2rem;
|
|
228
|
+
font-size: 0.75rem;
|
|
229
|
+
color: var(--text-dim);
|
|
230
|
+
text-transform: uppercase;
|
|
231
|
+
letter-spacing: 0.1em;
|
|
232
|
+
}
|
|
233
|
+
.page-btn {
|
|
234
|
+
background: none;
|
|
235
|
+
border: 1px solid var(--border);
|
|
236
|
+
color: var(--text);
|
|
237
|
+
padding: 0.4rem 1rem;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
transition: all 0.2s;
|
|
240
|
+
}
|
|
241
|
+
.page-btn:disabled { color: var(--text-dim); cursor: not-allowed; border-color: transparent; }
|
|
242
|
+
.page-btn:not(:disabled):hover { border-color: var(--text-dim); }
|
|
243
|
+
|
|
244
|
+
.empty { color: var(--text-dim); padding: 2rem 0; font-style: italic; font-size: 0.8rem; }
|
|
245
|
+
</style>
|
|
246
|
+
</head>
|
|
247
|
+
<body>
|
|
248
|
+
<div class="container">
|
|
249
|
+
<header>
|
|
250
|
+
<h1>deploy4me.dashboard</h1>
|
|
251
|
+
<div id="stat-uptime" class="subtitle">0s</div>
|
|
252
|
+
</header>
|
|
253
|
+
|
|
254
|
+
<div class="stats">
|
|
255
|
+
<div class="stat-item"><div class="stat-label">builds</div><div class="stat-value" id="stat-total">0</div></div>
|
|
256
|
+
<div class="stat-item"><div class="stat-label">success</div><div class="stat-value" id="stat-success">0</div></div>
|
|
257
|
+
<div class="stat-item"><div class="stat-label">failed</div><div class="stat-value" id="stat-failed">0</div></div>
|
|
258
|
+
<div class="stat-item"><div class="stat-label">active</div><div class="stat-value" id="stat-modules">0</div></div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<div class="section">
|
|
262
|
+
<div class="section-header"><div class="section-title">Active Modules</div></div>
|
|
263
|
+
<div id="modules"><div class="empty">Inquiring...</div></div>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div class="section">
|
|
267
|
+
<div class="section-header">
|
|
268
|
+
<div class="section-title">Build History</div>
|
|
269
|
+
<div class="tabs">
|
|
270
|
+
<button class="tab active" onclick="setFilter('all')">All</button>
|
|
271
|
+
<button class="tab" onclick="setFilter('building')">Building</button>
|
|
272
|
+
<button class="tab" onclick="setFilter('success')">Success</button>
|
|
273
|
+
<button class="tab" onclick="setFilter('error')">Failed</button>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
<div id="builds"></div>
|
|
277
|
+
<div class="pagination">
|
|
278
|
+
<button id="prev-btn" class="page-btn" onclick="changePage(-1)">Previous</button>
|
|
279
|
+
<span id="page-info">Page 1</span>
|
|
280
|
+
<button id="next-btn" class="page-btn" onclick="changePage(1)">Next</button>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<script>
|
|
286
|
+
let allBuilds = [];
|
|
287
|
+
let currentFilter = 'all';
|
|
288
|
+
let currentPage = 1;
|
|
289
|
+
const itemsPerPage = 8;
|
|
290
|
+
|
|
291
|
+
function formatUptime(ms) {
|
|
292
|
+
const s = Math.floor(ms / 1000);
|
|
293
|
+
const m = Math.floor(s / 60);
|
|
294
|
+
return m > 0 ? \`up \${m}m \${s%60}s\` : \`up \${s}s\`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function setFilter(filter) {
|
|
298
|
+
currentFilter = filter;
|
|
299
|
+
currentPage = 1;
|
|
300
|
+
document.querySelectorAll('.tab').forEach(t => {
|
|
301
|
+
t.classList.toggle('active', t.textContent.toLowerCase() === filter || (filter === 'error' && t.textContent === 'Failed'));
|
|
302
|
+
});
|
|
303
|
+
render();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function changePage(step) {
|
|
307
|
+
currentPage += step;
|
|
308
|
+
render();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function render() {
|
|
312
|
+
// Stats & Modules rendering
|
|
313
|
+
// ... (handled in fetchData)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function fetchData() {
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch('/api/state');
|
|
319
|
+
const data = await res.json();
|
|
320
|
+
|
|
321
|
+
// Update Stats
|
|
322
|
+
document.getElementById('stat-total').textContent = data.stats.totalBuilds;
|
|
323
|
+
document.getElementById('stat-success').textContent = data.stats.successfulBuilds;
|
|
324
|
+
document.getElementById('stat-failed').textContent = data.stats.failedBuilds;
|
|
325
|
+
document.getElementById('stat-modules').textContent = data.stats.activeModules;
|
|
326
|
+
document.getElementById('stat-uptime').textContent = formatUptime(data.stats.uptime);
|
|
327
|
+
|
|
328
|
+
// Update Modules
|
|
329
|
+
const modContainer = document.getElementById('modules');
|
|
330
|
+
modContainer.innerHTML = data.modules.map(mod => \`
|
|
331
|
+
<div class="list-item">
|
|
332
|
+
<div class="item-name">\${mod.name} <span style="color:var(--text-dim); font-size:0.7rem">v\${mod.version}</span></div>
|
|
333
|
+
<div class="status-text status-active">active</div>
|
|
334
|
+
</div>
|
|
335
|
+
\`).join('') || '<div class="empty">No active modules.</div>';
|
|
336
|
+
|
|
337
|
+
allBuilds = data.builds;
|
|
338
|
+
renderBuilds();
|
|
339
|
+
} catch (e) { console.error('Sync error'); }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function renderBuilds() {
|
|
343
|
+
const container = document.getElementById('builds');
|
|
344
|
+
|
|
345
|
+
// Filter
|
|
346
|
+
const filtered = allBuilds.filter(b => currentFilter === 'all' || b.status === currentFilter);
|
|
347
|
+
|
|
348
|
+
// Pagination
|
|
349
|
+
const totalPages = Math.ceil(filtered.length / itemsPerPage) || 1;
|
|
350
|
+
if (currentPage > totalPages) currentPage = totalPages;
|
|
351
|
+
|
|
352
|
+
const start = (currentPage - 1) * itemsPerPage;
|
|
353
|
+
const paginated = filtered.slice(start, start + itemsPerPage);
|
|
354
|
+
|
|
355
|
+
document.getElementById('page-info').textContent = \`Page \${currentPage} of \${totalPages}\`;
|
|
356
|
+
document.getElementById('prev-btn').disabled = currentPage === 1;
|
|
357
|
+
document.getElementById('next-btn').disabled = currentPage === totalPages;
|
|
358
|
+
|
|
359
|
+
if (!paginated.length) {
|
|
360
|
+
container.innerHTML = '<div class="empty">No records found for this filter.</div>';
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
container.innerHTML = paginated.map(build => \`
|
|
365
|
+
<div class="list-item">
|
|
366
|
+
<div>
|
|
367
|
+
<div class="item-name">\${build.moduleName}</div>
|
|
368
|
+
<div class="item-details">
|
|
369
|
+
<span>\${new Date(build.startTime).toLocaleTimeString('tr-TR')}</span>
|
|
370
|
+
<span>\${build.duration ? (build.duration/1000).toFixed(2)+'s' : '--'}</span>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="status-text status-\${build.status}">\${build.status}</div>
|
|
374
|
+
\${build.error ? \`<div class="error-log">\${build.error}</div>\` : ''}
|
|
375
|
+
</div>
|
|
376
|
+
\`).join('');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
fetchData();
|
|
380
|
+
setInterval(fetchData, 3000);
|
|
381
|
+
</script>
|
|
382
|
+
</body>
|
|
383
|
+
</html>`;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
exports.Dashboard = Dashboard;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Dashboard = exports.Monitor = void 0;
|
|
18
|
+
var monitor_1 = require("./monitor");
|
|
19
|
+
Object.defineProperty(exports, "Monitor", { enumerable: true, get: function () { return monitor_1.Monitor; } });
|
|
20
|
+
var dashboard_1 = require("./dashboard");
|
|
21
|
+
Object.defineProperty(exports, "Dashboard", { enumerable: true, get: function () { return dashboard_1.Dashboard; } });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BuildRecord, ActiveModule, MonitoringState } from './types';
|
|
2
|
+
export declare class Monitor {
|
|
3
|
+
private state;
|
|
4
|
+
private maxBuilds;
|
|
5
|
+
constructor();
|
|
6
|
+
startBuild(moduleName: string, modulePath: string): string;
|
|
7
|
+
completeBuild(id: string, status: 'success' | 'error', error?: string): void;
|
|
8
|
+
registerModule(name: string, version: string, routeCount: number): void;
|
|
9
|
+
unregisterModule(name: string): void;
|
|
10
|
+
getState(): MonitoringState;
|
|
11
|
+
getBuilds(): BuildRecord[];
|
|
12
|
+
getActiveModules(): ActiveModule[];
|
|
13
|
+
getStats(): {
|
|
14
|
+
totalBuilds: number;
|
|
15
|
+
successfulBuilds: number;
|
|
16
|
+
failedBuilds: number;
|
|
17
|
+
buildingNow: number;
|
|
18
|
+
activeModules: number;
|
|
19
|
+
uptime: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Monitor = void 0;
|
|
4
|
+
class Monitor {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.maxBuilds = 100;
|
|
7
|
+
this.state = {
|
|
8
|
+
builds: [],
|
|
9
|
+
activeModules: new Map(),
|
|
10
|
+
startTime: new Date()
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
startBuild(moduleName, modulePath) {
|
|
14
|
+
const id = `${moduleName}-${Date.now()}`;
|
|
15
|
+
const record = {
|
|
16
|
+
id,
|
|
17
|
+
moduleName,
|
|
18
|
+
modulePath,
|
|
19
|
+
status: 'building',
|
|
20
|
+
startTime: new Date()
|
|
21
|
+
};
|
|
22
|
+
this.state.builds.unshift(record);
|
|
23
|
+
if (this.state.builds.length > this.maxBuilds) {
|
|
24
|
+
this.state.builds = this.state.builds.slice(0, this.maxBuilds);
|
|
25
|
+
}
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
completeBuild(id, status, error) {
|
|
29
|
+
const build = this.state.builds.find(b => b.id === id);
|
|
30
|
+
if (build) {
|
|
31
|
+
build.status = status;
|
|
32
|
+
build.endTime = new Date();
|
|
33
|
+
build.duration = build.endTime.getTime() - build.startTime.getTime();
|
|
34
|
+
if (error) {
|
|
35
|
+
build.error = error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
registerModule(name, version, routeCount) {
|
|
40
|
+
this.state.activeModules.set(name, {
|
|
41
|
+
name,
|
|
42
|
+
version,
|
|
43
|
+
loadedAt: new Date(),
|
|
44
|
+
routeCount,
|
|
45
|
+
status: 'active'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
unregisterModule(name) {
|
|
49
|
+
this.state.activeModules.delete(name);
|
|
50
|
+
}
|
|
51
|
+
getState() {
|
|
52
|
+
return {
|
|
53
|
+
...this.state,
|
|
54
|
+
activeModules: new Map(this.state.activeModules)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
getBuilds() {
|
|
58
|
+
return [...this.state.builds];
|
|
59
|
+
}
|
|
60
|
+
getActiveModules() {
|
|
61
|
+
return Array.from(this.state.activeModules.values());
|
|
62
|
+
}
|
|
63
|
+
getStats() {
|
|
64
|
+
const builds = this.state.builds;
|
|
65
|
+
return {
|
|
66
|
+
totalBuilds: builds.length,
|
|
67
|
+
successfulBuilds: builds.filter(b => b.status === 'success').length,
|
|
68
|
+
failedBuilds: builds.filter(b => b.status === 'error').length,
|
|
69
|
+
buildingNow: builds.filter(b => b.status === 'building').length,
|
|
70
|
+
activeModules: this.state.activeModules.size,
|
|
71
|
+
uptime: Date.now() - this.state.startTime.getTime()
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.Monitor = Monitor;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface MonitoringConfig {
|
|
2
|
+
liveRunnerActions: boolean;
|
|
3
|
+
port?: number;
|
|
4
|
+
host?: string;
|
|
5
|
+
}
|
|
6
|
+
export type BuildStatus = 'building' | 'success' | 'error';
|
|
7
|
+
export interface BuildRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
moduleName: string;
|
|
10
|
+
modulePath: string;
|
|
11
|
+
status: BuildStatus;
|
|
12
|
+
startTime: Date;
|
|
13
|
+
endTime?: Date;
|
|
14
|
+
duration?: number;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ActiveModule {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
loadedAt: Date;
|
|
21
|
+
routeCount: number;
|
|
22
|
+
status: 'active' | 'error';
|
|
23
|
+
}
|
|
24
|
+
export interface MonitoringState {
|
|
25
|
+
builds: BuildRecord[];
|
|
26
|
+
activeModules: Map<string, ActiveModule>;
|
|
27
|
+
startTime: Date;
|
|
28
|
+
}
|