iikit-dashboard 1.4.0 → 1.5.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/bin/iikit-dashboard.js +4 -2
- package/package.json +1 -1
- package/src/public/index.html +18 -0
- package/src/server.js +36 -2
package/bin/iikit-dashboard.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const { createServer } = require('../src/server');
|
|
5
|
+
const { createServer, removePidfile } = require('../src/server');
|
|
6
6
|
|
|
7
7
|
// Parse arguments
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
|
-
let projectPath = process.cwd();
|
|
9
|
+
let projectPath = path.resolve(process.cwd());
|
|
10
10
|
let port = 3000;
|
|
11
11
|
|
|
12
12
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -46,6 +46,7 @@ async function main() {
|
|
|
46
46
|
// Handle graceful shutdown
|
|
47
47
|
process.on('SIGINT', () => {
|
|
48
48
|
console.log('\n Shutting down...');
|
|
49
|
+
removePidfile(projectPath);
|
|
49
50
|
result.server.close(() => {
|
|
50
51
|
if (result.watcher) result.watcher.close();
|
|
51
52
|
process.exit(0);
|
|
@@ -53,6 +54,7 @@ async function main() {
|
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
process.on('SIGTERM', () => {
|
|
57
|
+
removePidfile(projectPath);
|
|
56
58
|
result.server.close(() => {
|
|
57
59
|
if (result.watcher) result.watcher.close();
|
|
58
60
|
process.exit(0);
|
package/package.json
CHANGED
package/src/public/index.html
CHANGED
|
@@ -93,6 +93,17 @@
|
|
|
93
93
|
flex: 1 1 auto;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
.project-label {
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
color: var(--color-text-muted);
|
|
99
|
+
max-width: 160px;
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
text-overflow: ellipsis;
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
border-left: 1px solid var(--color-border);
|
|
104
|
+
padding-left: 12px;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
.feature-selector {
|
|
97
108
|
position: relative;
|
|
98
109
|
min-width: 100px;
|
|
@@ -2368,6 +2379,7 @@
|
|
|
2368
2379
|
<div class="logo-icon" aria-hidden="true">D</div>
|
|
2369
2380
|
<span>IIKit Dashboard</span>
|
|
2370
2381
|
</div>
|
|
2382
|
+
<div class="project-label" id="projectLabel" title=""></div>
|
|
2371
2383
|
<div class="feature-selector" role="navigation" aria-label="Feature selector">
|
|
2372
2384
|
<select id="featureSelect" aria-label="Select feature to display" tabindex="0">
|
|
2373
2385
|
<option value="">Loading features...</option>
|
|
@@ -5188,6 +5200,12 @@
|
|
|
5188
5200
|
applyTheme(themeMode);
|
|
5189
5201
|
|
|
5190
5202
|
// ====== Init ======
|
|
5203
|
+
fetch('/api/meta').then(r => r.json()).then(meta => {
|
|
5204
|
+
const label = document.getElementById('projectLabel');
|
|
5205
|
+
const dirName = meta.projectPath.split('/').pop();
|
|
5206
|
+
label.textContent = dirName;
|
|
5207
|
+
label.title = meta.projectPath;
|
|
5208
|
+
}).catch(() => {});
|
|
5191
5209
|
loadFeatures();
|
|
5192
5210
|
connectWebSocket();
|
|
5193
5211
|
})();
|
package/src/server.js
CHANGED
|
@@ -98,6 +98,33 @@ function getBoardState(projectPath, featureId) {
|
|
|
98
98
|
return { ...board, integrity };
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Write a pidfile with metadata so external scripts can identify this dashboard instance.
|
|
103
|
+
*/
|
|
104
|
+
function writePidfile(projectPath, port) {
|
|
105
|
+
const resolved = path.resolve(projectPath);
|
|
106
|
+
const specifyDir = path.join(resolved, '.specify');
|
|
107
|
+
fs.mkdirSync(specifyDir, { recursive: true });
|
|
108
|
+
const pidData = {
|
|
109
|
+
pid: process.pid,
|
|
110
|
+
port,
|
|
111
|
+
directory: resolved,
|
|
112
|
+
startedAt: new Date().toISOString()
|
|
113
|
+
};
|
|
114
|
+
fs.writeFileSync(path.join(specifyDir, 'dashboard.pid.json'), JSON.stringify(pidData, null, 2));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Remove the pidfile on shutdown.
|
|
119
|
+
*/
|
|
120
|
+
function removePidfile(projectPath) {
|
|
121
|
+
try {
|
|
122
|
+
fs.unlinkSync(path.join(path.resolve(projectPath), '.specify', 'dashboard.pid.json'));
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (err.code !== 'ENOENT') throw err;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
101
128
|
/**
|
|
102
129
|
* Create and configure the Express server with WebSocket support.
|
|
103
130
|
*
|
|
@@ -107,11 +134,17 @@ function getBoardState(projectPath, featureId) {
|
|
|
107
134
|
* @returns {Promise<{server: http.Server, port: number, wss: WebSocketServer}>}
|
|
108
135
|
*/
|
|
109
136
|
function createServer({ projectPath, port = 3000 }) {
|
|
137
|
+
const resolvedPath = path.resolve(projectPath);
|
|
110
138
|
const app = express();
|
|
111
139
|
|
|
112
140
|
// Serve static files from src/public
|
|
113
141
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
114
142
|
|
|
143
|
+
// API: project metadata
|
|
144
|
+
app.get('/api/meta', (req, res) => {
|
|
145
|
+
res.json({ projectPath: resolvedPath });
|
|
146
|
+
});
|
|
147
|
+
|
|
115
148
|
// API: list features
|
|
116
149
|
app.get('/api/features', (req, res) => {
|
|
117
150
|
try {
|
|
@@ -363,9 +396,10 @@ function createServer({ projectPath, port = 3000 }) {
|
|
|
363
396
|
return new Promise((resolve) => {
|
|
364
397
|
server.listen(port, () => {
|
|
365
398
|
const actualPort = server.address().port;
|
|
366
|
-
|
|
399
|
+
writePidfile(resolvedPath, actualPort);
|
|
400
|
+
resolve({ server, port: actualPort, wss, watcher, projectPath: resolvedPath });
|
|
367
401
|
});
|
|
368
402
|
});
|
|
369
403
|
}
|
|
370
404
|
|
|
371
|
-
module.exports = { createServer, listFeatures, getBoardState };
|
|
405
|
+
module.exports = { createServer, listFeatures, getBoardState, removePidfile };
|