cloud-ide-cide 2.0.40 → 2.0.42
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/appManager.js +146 -0
- package/cideShell.js +77 -13
- package/deployer/php/upload-ui.php +52 -3
- package/package.json +3 -3
- package/publishPackage.js +7 -4
package/appManager.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cide app — manage deployed apps remotely via the upload listener.
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* cide app --status Check if app is running
|
|
6
|
+
* cide app --start Start the app
|
|
7
|
+
* cide app --stop Stop the app
|
|
8
|
+
* cide app --restart Restart (stop + start)
|
|
9
|
+
* cide app --status -p 1 -s 1 Non-interactive
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const axios = require('axios');
|
|
13
|
+
const {
|
|
14
|
+
discoverUploadableProjects,
|
|
15
|
+
printProjectList,
|
|
16
|
+
selectProject,
|
|
17
|
+
selectServer,
|
|
18
|
+
getToken,
|
|
19
|
+
getAuthHeaders,
|
|
20
|
+
} = require('./uploadProject');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve app management endpoint URL.
|
|
24
|
+
* PHP uses ?action=app-status, Node uses /app/status routes.
|
|
25
|
+
*/
|
|
26
|
+
function resolveAppEndpoint(serverUrl, action) {
|
|
27
|
+
const u = new URL(serverUrl);
|
|
28
|
+
if (u.pathname.endsWith('.php')) {
|
|
29
|
+
u.searchParams.set('action', `app-${action}`);
|
|
30
|
+
return u.toString();
|
|
31
|
+
}
|
|
32
|
+
const base = u.pathname.replace(/\/upload\/?$/, '');
|
|
33
|
+
u.pathname = `${base}/app/${action}`;
|
|
34
|
+
return u.toString();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isPhpServer(serverUrl) {
|
|
38
|
+
return new URL(serverUrl).pathname.endsWith('.php');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function printStatus(data) {
|
|
42
|
+
console.log(` App : ${data.appCode}`);
|
|
43
|
+
if (data.type === 'php') {
|
|
44
|
+
console.log(` Type : PHP/UI (served by Apache/Nginx)`);
|
|
45
|
+
console.log(` Deployed : ${data.version ? 'Yes' : 'No'}`);
|
|
46
|
+
if (data.version) console.log(` Version : ${data.version}`);
|
|
47
|
+
if (data.deployedAt) console.log(` Date : ${data.deployedAt}`);
|
|
48
|
+
if (data.appUrl) console.log(` URL : ${data.appUrl}`);
|
|
49
|
+
if (data.releases) console.log(` Releases : ${data.releases}`);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(` Status : ${data.running ? `Running (PID: ${data.pid})` : 'Stopped'}`);
|
|
52
|
+
if (data.appDir) console.log(` Path : ${data.appDir}`);
|
|
53
|
+
if (data.logFile) console.log(` Log : ${data.logFile}`);
|
|
54
|
+
}
|
|
55
|
+
console.log('');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printAppResult(data, action) {
|
|
59
|
+
if (data.type === 'php') {
|
|
60
|
+
console.log(` ${data.message}`);
|
|
61
|
+
console.log('');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (action === 'stop') {
|
|
66
|
+
if (data.stopped) {
|
|
67
|
+
console.log(` Stopped (was PID: ${data.pid})`);
|
|
68
|
+
} else {
|
|
69
|
+
console.log(` ${data.reason || 'Not running'}`);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
if (data.started) {
|
|
73
|
+
console.log(` ${action === 'start' ? 'Started' : 'Restarted'} (PID: ${data.pid})`);
|
|
74
|
+
if (data.logFile) console.log(` Log : ${data.logFile}`);
|
|
75
|
+
} else {
|
|
76
|
+
console.log(` Failed to start: ${data.reason || 'unknown'}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (data.output) {
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(' ── App Console Output ──────────────────────');
|
|
83
|
+
console.log(data.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
84
|
+
console.log(' ─────────────────────────────────────────────');
|
|
85
|
+
}
|
|
86
|
+
console.log('');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function appManager(opts = {}) {
|
|
90
|
+
const action = opts.restart ? 'restart'
|
|
91
|
+
: opts.stop ? 'stop'
|
|
92
|
+
: opts.start ? 'start'
|
|
93
|
+
: 'status';
|
|
94
|
+
|
|
95
|
+
const projects = discoverUploadableProjects();
|
|
96
|
+
if (projects.length === 0) {
|
|
97
|
+
console.log('\nNo projects found with upload config in cide.json.\n');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (projects.length > 1) printProjectList(projects);
|
|
102
|
+
const project = projects.length === 1 ? projects[0] : await selectProject(projects, opts);
|
|
103
|
+
if (!project) return;
|
|
104
|
+
|
|
105
|
+
const server = await selectServer(project, opts);
|
|
106
|
+
if (!server) return;
|
|
107
|
+
|
|
108
|
+
const token = getToken(server);
|
|
109
|
+
const headers = getAuthHeaders(token);
|
|
110
|
+
const appCode = server.app_code || project.name;
|
|
111
|
+
const label = server.name || server.server_url;
|
|
112
|
+
|
|
113
|
+
console.log(`\n ${action.toUpperCase()} → ${project.name} on ${label}\n`);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
if (action === 'status') {
|
|
117
|
+
const url = resolveAppEndpoint(server.server_url, 'status') + `${isPhpServer(server.server_url) ? '&' : '?'}appCode=${encodeURIComponent(appCode)}`;
|
|
118
|
+
const { data } = await axios.get(url, { headers, timeout: 15000 });
|
|
119
|
+
printStatus(data);
|
|
120
|
+
|
|
121
|
+
} else if (action === 'stop') {
|
|
122
|
+
const url = resolveAppEndpoint(server.server_url, 'stop');
|
|
123
|
+
const { data } = await axios.post(url, { appCode }, { headers, timeout: 15000 });
|
|
124
|
+
printAppResult(data, 'stop');
|
|
125
|
+
|
|
126
|
+
} else if (action === 'start') {
|
|
127
|
+
const url = resolveAppEndpoint(server.server_url, 'start');
|
|
128
|
+
const { data } = await axios.post(url, { appCode }, { headers, timeout: 30000 });
|
|
129
|
+
printAppResult(data, 'start');
|
|
130
|
+
|
|
131
|
+
} else if (action === 'restart') {
|
|
132
|
+
const stopUrl = resolveAppEndpoint(server.server_url, 'stop');
|
|
133
|
+
await axios.post(stopUrl, { appCode }, { headers, timeout: 15000 }).catch(() => {});
|
|
134
|
+
|
|
135
|
+
const startUrl = resolveAppEndpoint(server.server_url, 'start');
|
|
136
|
+
const { data } = await axios.post(startUrl, { appCode }, { headers, timeout: 30000 });
|
|
137
|
+
printAppResult(data, 'restart');
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
const msg = err.response?.data?.message || err.message || err;
|
|
141
|
+
console.error(`\n Failed: ${msg}\n`);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = appManager;
|
package/cideShell.js
CHANGED
|
@@ -676,25 +676,62 @@ function handleBranch(ws, repoName) {
|
|
|
676
676
|
else runGitAll(ws, 'branch -vv', true);
|
|
677
677
|
}
|
|
678
678
|
|
|
679
|
-
|
|
680
|
-
|
|
679
|
+
const PID_FILE = '.cide_node_bg_pid.json';
|
|
680
|
+
|
|
681
|
+
function getPidFilePath(ws) {
|
|
682
|
+
if (ws && ws.nodeBackend) {
|
|
683
|
+
const dir = path.join(ws.nodeBackend.absPath, '.cide');
|
|
684
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
685
|
+
return path.join(dir, PID_FILE);
|
|
686
|
+
}
|
|
687
|
+
return path.join(process.cwd(), PID_FILE);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function savePid(ws, pid, command, args) {
|
|
691
|
+
const file = getPidFilePath(ws);
|
|
692
|
+
fs.writeFileSync(file, JSON.stringify({ pid, command, args, timestamp: Date.now() }));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function loadPid(ws) {
|
|
696
|
+
const file = getPidFilePath(ws);
|
|
697
|
+
if (fs.existsSync(file)) {
|
|
698
|
+
try {
|
|
699
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
700
|
+
} catch {}
|
|
701
|
+
}
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function clearPid(ws) {
|
|
706
|
+
const file = getPidFilePath(ws);
|
|
707
|
+
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function killBgServer(ws) {
|
|
711
|
+
const processInfo = loadPid(ws);
|
|
712
|
+
let targetPid = bgServerPid;
|
|
713
|
+
if (processInfo && processInfo.pid) {
|
|
714
|
+
targetPid = processInfo.pid;
|
|
715
|
+
}
|
|
716
|
+
if (!targetPid) {
|
|
681
717
|
console.log('No background server PID tracked (start with: server dev)');
|
|
682
718
|
return;
|
|
683
719
|
}
|
|
684
720
|
try {
|
|
685
721
|
if (process.platform === 'win32') {
|
|
686
|
-
execSync(`taskkill /PID ${
|
|
722
|
+
execSync(`taskkill /PID ${targetPid} /T /F`, { stdio: 'inherit', shell: true });
|
|
687
723
|
} else {
|
|
688
|
-
process.kill(-
|
|
724
|
+
process.kill(-targetPid, 'SIGTERM');
|
|
689
725
|
}
|
|
690
|
-
console.log(`Stopped process tree ${
|
|
726
|
+
console.log(`Stopped process tree ${targetPid}`);
|
|
691
727
|
} catch (e) {
|
|
692
|
-
console.error(
|
|
728
|
+
console.error(`Stop failed or process already killed: ${e.message}`);
|
|
693
729
|
}
|
|
694
730
|
bgServerPid = null;
|
|
731
|
+
clearPid(ws);
|
|
695
732
|
}
|
|
696
733
|
|
|
697
|
-
function spawnBg(cwd, command, args) {
|
|
734
|
+
function spawnBg(ws, cwd, command, args) {
|
|
698
735
|
const child = spawn(command, args, {
|
|
699
736
|
cwd,
|
|
700
737
|
shell: true,
|
|
@@ -703,6 +740,7 @@ function spawnBg(cwd, command, args) {
|
|
|
703
740
|
});
|
|
704
741
|
child.unref();
|
|
705
742
|
bgServerPid = child.pid;
|
|
743
|
+
savePid(ws, child.pid, command, args);
|
|
706
744
|
console.log(`Started background PID ${child.pid} (${command} ${args.join(' ')})`);
|
|
707
745
|
console.log('(Output not shown here; use server stop to end, or run npm in another terminal for logs.)');
|
|
708
746
|
}
|
|
@@ -743,18 +781,44 @@ async function handleServer(ws, sub, rest, rl) {
|
|
|
743
781
|
return;
|
|
744
782
|
}
|
|
745
783
|
if (sub === 'start') {
|
|
746
|
-
spawnBg(cwd, 'npm', ['run', 'start']);
|
|
784
|
+
spawnBg(ws, cwd, 'npm', ['run', 'start']);
|
|
747
785
|
return;
|
|
748
786
|
}
|
|
749
787
|
if (sub === 'dev') {
|
|
750
|
-
spawnBg(cwd, 'npm', ['run', 'dev']);
|
|
788
|
+
spawnBg(ws, cwd, 'npm', ['run', 'dev']);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (sub === 'status') {
|
|
792
|
+
const processInfo = loadPid(ws);
|
|
793
|
+
if (!processInfo) {
|
|
794
|
+
console.log('Server is NOT running (no PID tracked).');
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
let isRunning = false;
|
|
798
|
+
try {
|
|
799
|
+
if (process.platform === 'win32') {
|
|
800
|
+
const out = execSync(`tasklist /FI "PID eq ${processInfo.pid}"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
801
|
+
isRunning = out.includes(processInfo.pid.toString());
|
|
802
|
+
} else {
|
|
803
|
+
process.kill(processInfo.pid, 0);
|
|
804
|
+
isRunning = true;
|
|
805
|
+
}
|
|
806
|
+
} catch {
|
|
807
|
+
isRunning = false;
|
|
808
|
+
}
|
|
809
|
+
if (isRunning) {
|
|
810
|
+
console.log(`Server is RUNNING (PID: ${processInfo.pid}, Command: ${processInfo.command} ${processInfo.args.join(' ')})`);
|
|
811
|
+
} else {
|
|
812
|
+
console.log(`Server was tracked as PID: ${processInfo.pid} but appears to be dead now.`);
|
|
813
|
+
clearPid(ws);
|
|
814
|
+
}
|
|
751
815
|
return;
|
|
752
816
|
}
|
|
753
817
|
if (sub === 'stop') {
|
|
754
|
-
killBgServer();
|
|
818
|
+
killBgServer(ws);
|
|
755
819
|
return;
|
|
756
820
|
}
|
|
757
|
-
console.error('Usage: server build | start | dev | stop | init | listener start|stop|status|restart');
|
|
821
|
+
console.error('Usage: server build | start | dev | stop | status | init | listener start|stop|status|restart');
|
|
758
822
|
}
|
|
759
823
|
|
|
760
824
|
function handleSeed(ws, name) {
|
|
@@ -796,7 +860,7 @@ function handleServe(ws) {
|
|
|
796
860
|
console.error(`No "start" script in ${path.basename(ws.angularRoot)}/package.json`);
|
|
797
861
|
return;
|
|
798
862
|
}
|
|
799
|
-
spawnBg(ws.angularRoot, 'npm', ['run', 'start']);
|
|
863
|
+
spawnBg(ws, ws.angularRoot, 'npm', ['run', 'start']);
|
|
800
864
|
}
|
|
801
865
|
|
|
802
866
|
function handleTailwind(ws) {
|
|
@@ -805,7 +869,7 @@ function handleTailwind(ws) {
|
|
|
805
869
|
console.error(`No "tailwindcss" script in ${path.basename(ws.angularRoot)}/package.json`);
|
|
806
870
|
return;
|
|
807
871
|
}
|
|
808
|
-
spawnBg(ws.angularRoot, 'npm', ['run', 'tailwindcss']);
|
|
872
|
+
spawnBg(ws, ws.angularRoot, 'npm', ['run', 'tailwindcss']);
|
|
809
873
|
}
|
|
810
874
|
|
|
811
875
|
function handleLint(ws) {
|
|
@@ -59,9 +59,12 @@ $deploymentsFile = "{$cideDir}/deployments.json";
|
|
|
59
59
|
// ── Route ────────────────────────────────────────────────────────────────────
|
|
60
60
|
$action = $_GET['action'] ?? $_POST['action'] ?? 'upload';
|
|
61
61
|
|
|
62
|
-
if ($action === 'history')
|
|
63
|
-
elseif ($action === 'rollback')
|
|
64
|
-
|
|
62
|
+
if ($action === 'history') handleHistory();
|
|
63
|
+
elseif ($action === 'rollback') handleRollback();
|
|
64
|
+
elseif ($action === 'app-status') handleAppStatus();
|
|
65
|
+
elseif ($action === 'app-start') handleAppManageNA('start');
|
|
66
|
+
elseif ($action === 'app-stop') handleAppManageNA('stop');
|
|
67
|
+
else handleUpload();
|
|
65
68
|
|
|
66
69
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
67
70
|
// ── Upload ────────────────────────────────────────────────────────────────────
|
|
@@ -238,6 +241,52 @@ function handleRollback() {
|
|
|
238
241
|
]);
|
|
239
242
|
}
|
|
240
243
|
|
|
244
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
245
|
+
// ── App Status ───────────────────────────────────────────────────────────────
|
|
246
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
247
|
+
function handleAppStatus() {
|
|
248
|
+
global $currentDir;
|
|
249
|
+
|
|
250
|
+
$appCode = isset($_GET['appCode']) ? preg_replace('/[^a-zA-Z0-9\-_]/', '', $_GET['appCode']) : '';
|
|
251
|
+
if ($appCode === '') {
|
|
252
|
+
http_response_code(422);
|
|
253
|
+
echo json_encode(['status' => 'FAILED', 'message' => 'Missing appCode']);
|
|
254
|
+
exit;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
$deployments = loadDeployments();
|
|
258
|
+
$current = $deployments['apps'][$appCode]['current'] ?? null;
|
|
259
|
+
$currentPath = "{$currentDir}/{$appCode}";
|
|
260
|
+
$deployed = is_dir($currentPath) || is_link($currentPath);
|
|
261
|
+
|
|
262
|
+
// Build the app URL from server info
|
|
263
|
+
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
|
264
|
+
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
|
|
265
|
+
// Strip port from host if present, UI runs on default 80/443
|
|
266
|
+
$host = preg_replace('/:\d+$/', '', $host);
|
|
267
|
+
$appUrl = "{$protocol}://{$host}";
|
|
268
|
+
|
|
269
|
+
echo json_encode([
|
|
270
|
+
'status' => 'SUCCESS',
|
|
271
|
+
'appCode' => $appCode,
|
|
272
|
+
'running' => $deployed,
|
|
273
|
+
'type' => 'php',
|
|
274
|
+
'appUrl' => $appUrl,
|
|
275
|
+
'appDir' => $currentPath,
|
|
276
|
+
'version' => $current['version'] ?? null,
|
|
277
|
+
'deployedAt' => $current['deployedAt'] ?? null,
|
|
278
|
+
'releases' => count($deployments['apps'][$appCode]['history'] ?? []),
|
|
279
|
+
]);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function handleAppManageNA($action) {
|
|
283
|
+
echo json_encode([
|
|
284
|
+
'status' => 'SUCCESS',
|
|
285
|
+
'message' => "PHP/UI apps are served by Apache/Nginx — no process to {$action}. The app is always running when deployed.",
|
|
286
|
+
'type' => 'php',
|
|
287
|
+
]);
|
|
288
|
+
}
|
|
289
|
+
|
|
241
290
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
242
291
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
243
292
|
// ═══════════════════════════════════════════════════════════════════════════════
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloud-ide-cide",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.42",
|
|
4
4
|
"description": "Cloud IDE CLI — create, build, publish, upload and deploy Cloud IDE projects.",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"resolveNgProjectName.js",
|
|
20
20
|
"uploadProject.js",
|
|
21
21
|
"serverInit.js",
|
|
22
|
+
"appManager.js",
|
|
22
23
|
"deployer/"
|
|
23
24
|
],
|
|
24
25
|
"dependencies": {
|
|
@@ -48,6 +49,5 @@
|
|
|
48
49
|
],
|
|
49
50
|
"author": "Ankush Shivlal Bhure",
|
|
50
51
|
"license": "ISC",
|
|
51
|
-
"repository": {
|
|
52
|
-
}
|
|
52
|
+
"repository": {}
|
|
53
53
|
}
|
package/publishPackage.js
CHANGED
|
@@ -41,9 +41,12 @@ function writeJson(p, obj) {
|
|
|
41
41
|
fs.writeFileSync(p, JSON.stringify(obj, null, 2) + '\n', 'utf8');
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
function isPublishable(pkg) {
|
|
45
|
+
if (!pkg || !pkg.publishConfig) return false;
|
|
46
|
+
const r = pkg.publishConfig.registry;
|
|
47
|
+
if (typeof r === 'string' && (r.includes(GPR_HOST) || r.includes('npmjs.org'))) return true;
|
|
48
|
+
if (pkg.publishConfig.access === 'public') return true;
|
|
49
|
+
return false;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
function bumpSemver(version, kind) {
|
|
@@ -135,7 +138,7 @@ function discoverPublishablePackages({ cide, angularRoot, monorepoRoot }) {
|
|
|
135
138
|
} catch {
|
|
136
139
|
return;
|
|
137
140
|
}
|
|
138
|
-
if (!
|
|
141
|
+
if (!isPublishable(pkg)) return;
|
|
139
142
|
seen.add(resolved);
|
|
140
143
|
const folderName = path.basename(absDir);
|
|
141
144
|
list.push({
|