cloud-ide-cide 2.0.36 → 2.0.38

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.
@@ -121,63 +121,99 @@ function stopApp(appCode) {
121
121
  }
122
122
 
123
123
  function startApp(appCode) {
124
- const appDir = path.join(SERVER_ROOT, appCode);
125
- const entryFile = path.join(appDir, 'server.js');
124
+ return new Promise((resolve) => {
125
+ const appDir = path.join(SERVER_ROOT, appCode);
126
+ const entryFile = path.join(appDir, 'server.js');
126
127
 
127
- if (!fs.existsSync(entryFile)) {
128
- return { started: false, reason: `Entry file not found: ${entryFile}` };
129
- }
128
+ if (!fs.existsSync(entryFile)) {
129
+ return resolve({ started: false, reason: `Entry file not found: ${entryFile}` });
130
+ }
130
131
 
131
- // Stop existing process first
132
- stopApp(appCode);
132
+ // Stop existing process first
133
+ stopApp(appCode);
133
134
 
134
- const logFile = getAppLogFile(appCode);
135
- fs.mkdirSync(path.dirname(logFile), { recursive: true });
136
- const out = fs.openSync(logFile, 'a');
137
- const err = fs.openSync(logFile, 'a');
138
-
139
- const child = spawn('node', [entryFile], {
140
- cwd: appDir,
141
- detached: true,
142
- stdio: ['ignore', out, err],
143
- env: { ...process.env, NODE_ENV: 'production' },
144
- });
135
+ const logFile = getAppLogFile(appCode);
136
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
137
+
138
+ // Truncate log so we capture only this run's output
139
+ fs.writeFileSync(logFile, '');
140
+
141
+ const out = fs.openSync(logFile, 'a');
142
+ const err = fs.openSync(logFile, 'a');
143
+
144
+ const child = spawn('node', [entryFile], {
145
+ cwd: appDir,
146
+ detached: true,
147
+ stdio: ['ignore', out, err],
148
+ env: { ...process.env, NODE_ENV: 'production' },
149
+ });
145
150
 
146
- child.unref();
151
+ child.unref();
147
152
 
148
- // Save PID
149
- fs.writeFileSync(getAppPidFile(appCode), String(child.pid));
150
- appendLog(`START app=${appCode} pid=${child.pid} entry=${entryFile}`);
153
+ // Save PID
154
+ fs.writeFileSync(getAppPidFile(appCode), String(child.pid));
155
+ appendLog(`START app=${appCode} pid=${child.pid} entry=${entryFile}`);
151
156
 
152
- return { started: true, pid: child.pid, logFile };
157
+ // Wait 3 seconds then check if process is still alive & read its output
158
+ setTimeout(() => {
159
+ const alive = isProcessRunning(child.pid);
160
+ let output = '';
161
+ try { output = fs.readFileSync(logFile, 'utf8').slice(-2000); } catch {}
162
+
163
+ if (!alive) {
164
+ try { fs.unlinkSync(getAppPidFile(appCode)); } catch {}
165
+ }
166
+
167
+ resolve({
168
+ started: alive,
169
+ pid: alive ? child.pid : null,
170
+ logFile,
171
+ output,
172
+ reason: alive ? null : 'Process exited within 3s (see output)',
173
+ });
174
+ }, 3000);
175
+ });
153
176
  }
154
177
 
155
- function installAndStart(appCode) {
178
+ function cleanDir(dir) {
179
+ if (!fs.existsSync(dir)) return;
180
+ for (const entry of fs.readdirSync(dir)) {
181
+ // preserve node_modules to speed up npm install
182
+ if (entry === 'node_modules') continue;
183
+ const fullPath = path.join(dir, entry);
184
+ fs.rmSync(fullPath, { recursive: true, force: true });
185
+ }
186
+ }
187
+
188
+ async function installAndStart(appCode) {
156
189
  const appDir = path.join(SERVER_ROOT, appCode);
157
190
  const steps = [];
191
+ let npmOutput = '';
158
192
 
159
193
  // npm install if package.json exists
160
194
  const pkgPath = path.join(appDir, 'package.json');
161
195
  if (fs.existsSync(pkgPath)) {
162
196
  try {
163
- execSync('npm install --production', { cwd: appDir, stdio: 'pipe', shell: true, timeout: 120000 });
197
+ const out = execSync('npm install --production 2>&1', { cwd: appDir, encoding: 'utf8', shell: true, timeout: 120000 });
198
+ npmOutput = (out || '').slice(-1000);
164
199
  steps.push('npm install: ok');
165
200
  appendLog(`INSTALL app=${appCode} npm install ok`);
166
201
  } catch (e) {
167
- steps.push(`npm install: failed (${e.message})`);
202
+ npmOutput = (e.stdout || '').slice(-500) + '\n' + (e.stderr || '').slice(-500);
203
+ steps.push(`npm install: failed`);
168
204
  appendLog(`INSTALL app=${appCode} npm install FAILED: ${e.message}`);
169
205
  }
170
206
  }
171
207
 
172
208
  // Start the app
173
- const result = startApp(appCode);
209
+ const result = await startApp(appCode);
174
210
  if (result.started) {
175
211
  steps.push(`started: pid ${result.pid}`);
176
212
  } else {
177
213
  steps.push(`start failed: ${result.reason}`);
178
214
  }
179
215
 
180
- return { steps, ...result };
216
+ return { steps, npmOutput, ...result };
181
217
  }
182
218
 
183
219
  // ── Auth middleware ───────────────────────────────────────────────────────────
@@ -196,7 +232,7 @@ function authGuard(req, res, next) {
196
232
  // ═══════════════════════════════════════════════════════════════════════════════
197
233
  // ── Upload ────────────────────────────────────────────────────────────────────
198
234
  // ═══════════════════════════════════════════════════════════════════════════════
199
- app.post('/upload', authGuard, upload.single('file'), (req, res) => {
235
+ app.post('/upload', authGuard, upload.single('file'), async (req, res) => {
200
236
  try {
201
237
  if (!req.file) {
202
238
  return res.status(400).json({ status: 'FAILED', message: 'No file uploaded.' });
@@ -222,14 +258,15 @@ app.post('/upload', authGuard, upload.single('file'), (req, res) => {
222
258
  const releaseZipPath = path.join(releaseDir, `${version}.zip`);
223
259
  fs.copyFileSync(req.file.path, releaseZipPath);
224
260
 
225
- // ── Extract directly to {server_root}/{appCode}/ ─────────────────────
261
+ // ── Clean old files & extract to {server_root}/{appCode}/ ─────────────
226
262
  fs.mkdirSync(appDir, { recursive: true });
263
+ cleanDir(appDir);
227
264
  const zip = new AdmZip(req.file.path);
228
265
  zip.extractAllTo(appDir, true);
229
266
  fs.unlinkSync(req.file.path);
230
267
 
231
268
  // ── Install dependencies & start the app ─────────────────────────────
232
- const appResult = installAndStart(appCode);
269
+ const appResult = await installAndStart(appCode);
233
270
 
234
271
  // ── Track state ──────────────────────────────────────────────────────
235
272
  const deployments = loadDeployments();
@@ -298,7 +335,7 @@ app.get('/history', authGuard, (req, res) => {
298
335
  // ═══════════════════════════════════════════════════════════════════════════════
299
336
  // ── Rollback ──────────────────────────────────────────────────────────────────
300
337
  // ═══════════════════════════════════════════════════════════════════════════════
301
- app.post('/rollback', authGuard, (req, res) => {
338
+ app.post('/rollback', authGuard, async (req, res) => {
302
339
  try {
303
340
  const appCode = (req.body.appCode || '').replace(/[^a-zA-Z0-9\-_]/g, '');
304
341
  const version = (req.body.version || '').replace(/[^a-zA-Z0-9\.\+\-_]/g, '');
@@ -316,13 +353,14 @@ app.post('/rollback', authGuard, (req, res) => {
316
353
  const deployments = loadDeployments();
317
354
  const previousVersion = deployments.apps?.[appCode]?.current?.version || null;
318
355
 
319
- // Re-extract the zip to the app directory
356
+ // Clean old files & re-extract the zip
320
357
  fs.mkdirSync(appDir, { recursive: true });
358
+ cleanDir(appDir);
321
359
  const zip = new AdmZip(releaseZipPath);
322
360
  zip.extractAllTo(appDir, true);
323
361
 
324
362
  // Install dependencies & restart the app
325
- const appResult = installAndStart(appCode);
363
+ const appResult = await installAndStart(appCode);
326
364
 
327
365
  const record = {
328
366
  version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloud-ide-cide",
3
- "version": "2.0.36",
3
+ "version": "2.0.38",
4
4
  "description": "Cloud IDE CLI — create, build, publish, upload and deploy Cloud IDE projects.",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/serverInit.js CHANGED
@@ -256,6 +256,7 @@ function listenerStatus(cideDir) {
256
256
  }
257
257
 
258
258
  console.log('');
259
+ console.log(' ── Upload Listener ──────────────────────────');
259
260
  console.log(` Server type : ${config.serverType}`);
260
261
  console.log(` Server root : ${config.serverRoot}`);
261
262
  console.log(` Port : ${config.port || 4500}`);
@@ -275,6 +276,41 @@ function listenerStatus(cideDir) {
275
276
  }
276
277
  console.log(` Log : ${getServerLog(cideDir)}`);
277
278
  }
279
+
280
+ // ── Show deployed apps status ─────────────────────────────────────────
281
+ const deployments = safeReadJson(path.join(cideDir, 'deployments.json'));
282
+ if (deployments && deployments.apps && Object.keys(deployments.apps).length > 0) {
283
+ console.log('');
284
+ console.log(' ── Deployed Apps ────────────────────────────');
285
+
286
+ for (const [appCode, appData] of Object.entries(deployments.apps)) {
287
+ const pidFile = path.join(cideDir, 'logs', `${appCode}.pid`);
288
+ let appPid = null;
289
+ try { appPid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10); } catch {}
290
+ if (isNaN(appPid)) appPid = null;
291
+
292
+ const appRunning = appPid ? isProcessRunning(appPid) : false;
293
+ const current = appData.current || {};
294
+ const appLogFile = path.join(cideDir, 'logs', `${appCode}.log`);
295
+
296
+ console.log('');
297
+ console.log(` App : ${appCode}`);
298
+ console.log(` Path : ${current.appDir || 'N/A'}`);
299
+ console.log(` Version : ${current.version || 'N/A'}`);
300
+ console.log(` Deployed : ${current.deployedAt || 'N/A'}`);
301
+ if (appRunning) {
302
+ console.log(` Status : Running (PID: ${appPid})`);
303
+ } else {
304
+ console.log(' Status : Stopped');
305
+ if (appPid) {
306
+ try { fs.unlinkSync(pidFile); } catch {}
307
+ }
308
+ }
309
+ console.log(` Log : ${appLogFile}`);
310
+ console.log(` Releases : ${appData.history ? appData.history.length : 0}`);
311
+ }
312
+ }
313
+
278
314
  console.log('');
279
315
  }
280
316
 
package/uploadProject.js CHANGED
@@ -629,6 +629,17 @@ async function uploadProject(opts = {}) {
629
629
  console.log(' Rollback successful!');
630
630
  console.log(` Now active : ${result.rolledBackTo}`);
631
631
  console.log(` Previous : ${result.previousVersion}`);
632
+ if (result.appProcess) {
633
+ if (result.appProcess.started) {
634
+ console.log(` App : Running (PID: ${result.appProcess.pid})`);
635
+ } else {
636
+ console.log(` App : Failed to start`);
637
+ if (result.appProcess.reason) console.log(` Reason : ${result.appProcess.reason}`);
638
+ }
639
+ if (result.appProcess.steps) {
640
+ result.appProcess.steps.forEach(s => console.log(` Step : ${s}`));
641
+ }
642
+ }
632
643
  console.log('');
633
644
  } else {
634
645
  console.error(` Rollback failed: ${result.message || JSON.stringify(result)}`);
@@ -704,7 +715,33 @@ async function uploadProject(opts = {}) {
704
715
  console.log(` App : ${result.appCode}`);
705
716
  console.log(` Server : ${label}`);
706
717
  if (result.previousVersion) console.log(` Previous : ${result.previousVersion}`);
707
- if (result.releasePath) console.log(` Path : ${result.releasePath}`);
718
+ if (result.appDir) console.log(` Path : ${result.appDir}`);
719
+ else if (result.releasePath) console.log(` Path : ${result.releasePath}`);
720
+ if (result.appProcess) {
721
+ console.log('');
722
+ if (result.appProcess.started) {
723
+ console.log(` App : Running (PID: ${result.appProcess.pid})`);
724
+ if (result.appProcess.logFile) console.log(` App Log : ${result.appProcess.logFile}`);
725
+ } else {
726
+ console.log(` App : Failed to start`);
727
+ if (result.appProcess.reason) console.log(` Reason : ${result.appProcess.reason}`);
728
+ }
729
+ if (result.appProcess.steps) {
730
+ result.appProcess.steps.forEach(s => console.log(` Step : ${s}`));
731
+ }
732
+ if (result.appProcess.output) {
733
+ console.log('');
734
+ console.log(' ── App Console Output ──────────────────────');
735
+ console.log(result.appProcess.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
736
+ console.log(' ─────────────────────────────────────────────');
737
+ }
738
+ if (result.appProcess.npmOutput) {
739
+ console.log('');
740
+ console.log(' ── npm install Output ──────────────────────');
741
+ console.log(result.appProcess.npmOutput.trim().split('\n').map(l => ` ${l}`).join('\n'));
742
+ console.log(' ─────────────────────────────────────────────');
743
+ }
744
+ }
708
745
  console.log('');
709
746
  } else {
710
747
  console.error(` Upload failed: ${result.message || JSON.stringify(result)}`);