cloud-ide-cide 2.0.34

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.
@@ -0,0 +1,294 @@
1
+ <?php
2
+ /**
3
+ * Cloud IDE — UI code upload receiver.
4
+ * Lives at: {server_root}/.cide/scripts/upload-ui.php
5
+ *
6
+ * Reads config from: ../.cide/config.json (token, paths)
7
+ * Writes state to: ../.cide/deployments.json
8
+ * Stores releases in: ../.cide/releases/{appCode}/{version}/
9
+ * Stores backups in: ../.cide/backups/{appCode}/{version}.zip
10
+ * Logs to: ../.cide/logs/deploy.log
11
+ * Symlinks: {server_root}/current/{appCode} → active release
12
+ *
13
+ * Endpoints (all require Authorization: Bearer <token>):
14
+ *
15
+ * POST ?action=upload (multipart/form-data)
16
+ * - file, appCode, environment
17
+ *
18
+ * GET ?action=history&appCode=xxx
19
+ *
20
+ * POST ?action=rollback
21
+ * - JSON body: { "appCode", "version" }
22
+ */
23
+
24
+ header('Content-Type: application/json');
25
+
26
+ // ── Resolve .cide/ root ──────────────────────────────────────────────────────
27
+ // PHP_UI_DEPLOY_BASE_PATH env overrides; otherwise derive from script location
28
+ $cideDir = getenv('PHP_UI_DEPLOY_BASE_PATH') ?: dirname(__DIR__);
29
+ $configPath = "{$cideDir}/config.json";
30
+
31
+ $config = null;
32
+ if (file_exists($configPath)) {
33
+ $config = json_decode(file_get_contents($configPath), true);
34
+ }
35
+
36
+ $expectedToken = $config['token'] ?? getenv('PHP_UI_DEPLOY_TOKEN') ?: '';
37
+
38
+ // ── Auth ──────────────────────────────────────────────────────────────────────
39
+ if (!$expectedToken) {
40
+ http_response_code(500);
41
+ echo json_encode(['status' => 'FAILED', 'message' => 'Server not configured. Run: cide server-init']);
42
+ exit;
43
+ }
44
+
45
+ $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
46
+ if (!preg_match('/Bearer\s+(.*)$/i', $authHeader, $m) || $m[1] !== $expectedToken) {
47
+ http_response_code(401);
48
+ echo json_encode(['status' => 'FAILED', 'message' => 'Unauthorized upload request.']);
49
+ exit;
50
+ }
51
+
52
+ // ── Paths from config or defaults ────────────────────────────────────────────
53
+ $releasesDir = $config['paths']['releases'] ?? "{$cideDir}/releases";
54
+ $backupsDir = $config['paths']['backups'] ?? "{$cideDir}/backups";
55
+ $logsDir = $config['paths']['logs'] ?? "{$cideDir}/logs";
56
+ $currentDir = $config['paths']['current'] ?? dirname($cideDir) . '/current';
57
+ $deploymentsFile = "{$cideDir}/deployments.json";
58
+
59
+ // ── Route ────────────────────────────────────────────────────────────────────
60
+ $action = $_GET['action'] ?? $_POST['action'] ?? 'upload';
61
+
62
+ if ($action === 'history') handleHistory();
63
+ elseif ($action === 'rollback') handleRollback();
64
+ else handleUpload();
65
+
66
+ // ═══════════════════════════════════════════════════════════════════════════════
67
+ // ── Upload ────────────────────────────────────────────────────────────────────
68
+ // ═══════════════════════════════════════════════════════════════════════════════
69
+ function handleUpload() {
70
+ global $releasesDir, $backupsDir, $currentDir;
71
+
72
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
73
+ http_response_code(405);
74
+ echo json_encode(['status' => 'FAILED', 'message' => 'Only POST allowed.']);
75
+ exit;
76
+ }
77
+
78
+ if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
79
+ $code = $_FILES['file']['error'] ?? 'no file';
80
+ http_response_code(400);
81
+ echo json_encode(['status' => 'FAILED', 'message' => "File upload failed (code: {$code})."]);
82
+ exit;
83
+ }
84
+
85
+ $appCode = isset($_POST['appCode']) ? preg_replace('/[^a-zA-Z0-9\-_]/', '', $_POST['appCode']) : '';
86
+ $environment = isset($_POST['environment']) ? preg_replace('/[^a-zA-Z0-9\-_]/', '', $_POST['environment']) : 'production';
87
+ $deployMsg = isset($_POST['message']) ? substr(trim($_POST['message']), 0, 500) : '';
88
+
89
+ if ($appCode === '') {
90
+ http_response_code(422);
91
+ echo json_encode(['status' => 'FAILED', 'message' => 'Missing required field: appCode']);
92
+ exit;
93
+ }
94
+
95
+ $version = date('Ymd_His');
96
+ $releasePath = "{$releasesDir}/{$appCode}/{$version}";
97
+ $currentPath = "{$currentDir}/{$appCode}";
98
+ $tmpZip = $_FILES['file']['tmp_name'];
99
+
100
+ // ── Save backup zip ──────────────────────────────────────────────────
101
+ $backupDir = "{$backupsDir}/{$appCode}";
102
+ @mkdir($backupDir, 0775, true);
103
+ copy($tmpZip, "{$backupDir}/{$version}.zip");
104
+
105
+ // ── Extract ──────────────────────────────────────────────────────────
106
+ $zip = new ZipArchive();
107
+ if ($zip->open($tmpZip) !== true) {
108
+ http_response_code(400);
109
+ echo json_encode(['status' => 'FAILED', 'message' => 'Uploaded file is not a valid zip archive.']);
110
+ exit;
111
+ }
112
+ @mkdir($releasePath, 0775, true);
113
+ $zip->extractTo($releasePath);
114
+ $zip->close();
115
+
116
+ // ── Track state ──────────────────────────────────────────────────────
117
+ $deployments = loadDeployments();
118
+ $previousVersion = $deployments['apps'][$appCode]['current']['version'] ?? null;
119
+
120
+ // ── Switch symlink ───────────────────────────────────────────────────
121
+ switchCurrent($currentPath, $releasePath);
122
+
123
+ // ── Record ───────────────────────────────────────────────────────────
124
+ $record = [
125
+ 'version' => $version,
126
+ 'environment' => $environment,
127
+ 'releasePath' => $releasePath,
128
+ 'deployedAt' => date(DATE_ATOM),
129
+ 'action' => 'upload',
130
+ 'message' => $deployMsg,
131
+ 'previousVersion' => $previousVersion,
132
+ ];
133
+
134
+ if (!isset($deployments['apps'][$appCode])) {
135
+ $deployments['apps'][$appCode] = ['current' => null, 'history' => []];
136
+ }
137
+ $deployments['apps'][$appCode]['current'] = $record;
138
+ $deployments['apps'][$appCode]['history'][] = $record;
139
+ saveDeployments($deployments);
140
+
141
+ appendLog("UPLOAD app={$appCode} version={$version} prev={$previousVersion} env={$environment} msg={$deployMsg}");
142
+
143
+ echo json_encode([
144
+ 'status' => 'SUCCESS',
145
+ 'message' => 'UI code uploaded and deployed.',
146
+ 'appCode' => $appCode,
147
+ 'environment' => $environment,
148
+ 'releaseVersion' => $version,
149
+ 'previousVersion' => $previousVersion,
150
+ 'releasePath' => $releasePath,
151
+ 'completedAt' => date(DATE_ATOM),
152
+ ]);
153
+ }
154
+
155
+ // ═══════════════════════════════════════════════════════════════════════════════
156
+ // ── History ───────────────────────────────────────────────────────────────────
157
+ // ═══════════════════════════════════════════════════════════════════════════════
158
+ function handleHistory() {
159
+ $appCode = isset($_GET['appCode']) ? preg_replace('/[^a-zA-Z0-9\-_]/', '', $_GET['appCode']) : '';
160
+ if ($appCode === '') {
161
+ http_response_code(422);
162
+ echo json_encode(['status' => 'FAILED', 'message' => 'Missing required query param: appCode']);
163
+ exit;
164
+ }
165
+
166
+ $deployments = loadDeployments();
167
+ $appData = $deployments['apps'][$appCode] ?? ['current' => null, 'history' => []];
168
+
169
+ echo json_encode([
170
+ 'status' => 'SUCCESS',
171
+ 'appCode' => $appCode,
172
+ 'current' => $appData['current'],
173
+ 'history' => $appData['history'],
174
+ 'totalReleases' => count($appData['history']),
175
+ ]);
176
+ }
177
+
178
+ // ═══════════════════════════════════════════════════════════════════════════════
179
+ // ── Rollback ──────────────────────────────────────────────────────────────────
180
+ // ═══════════════════════════════════════════════════════════════════════════════
181
+ function handleRollback() {
182
+ global $releasesDir, $currentDir;
183
+
184
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
185
+ http_response_code(405);
186
+ echo json_encode(['status' => 'FAILED', 'message' => 'Only POST allowed.']);
187
+ exit;
188
+ }
189
+
190
+ $payload = json_decode(file_get_contents('php://input'), true);
191
+ $appCode = isset($payload['appCode']) ? preg_replace('/[^a-zA-Z0-9\-_]/', '', $payload['appCode']) : '';
192
+ $version = isset($payload['version']) ? preg_replace('/[^a-zA-Z0-9\.\+\-_]/', '', $payload['version']) : '';
193
+
194
+ if ($appCode === '' || $version === '') {
195
+ http_response_code(422);
196
+ echo json_encode(['status' => 'FAILED', 'message' => 'Missing required fields: appCode, version']);
197
+ exit;
198
+ }
199
+
200
+ $releasePath = "{$releasesDir}/{$appCode}/{$version}";
201
+ if (!is_dir($releasePath)) {
202
+ http_response_code(404);
203
+ echo json_encode(['status' => 'FAILED', 'message' => "Release not found: {$version}"]);
204
+ exit;
205
+ }
206
+
207
+ $currentPath = "{$currentDir}/{$appCode}";
208
+ $deployments = loadDeployments();
209
+ $previousVersion = $deployments['apps'][$appCode]['current']['version'] ?? null;
210
+
211
+ switchCurrent($currentPath, $releasePath);
212
+
213
+ $record = [
214
+ 'version' => $version,
215
+ 'environment' => $deployments['apps'][$appCode]['current']['environment'] ?? 'production',
216
+ 'releasePath' => $releasePath,
217
+ 'deployedAt' => date(DATE_ATOM),
218
+ 'action' => 'rollback',
219
+ 'previousVersion' => $previousVersion,
220
+ ];
221
+
222
+ if (!isset($deployments['apps'][$appCode])) {
223
+ $deployments['apps'][$appCode] = ['current' => null, 'history' => []];
224
+ }
225
+ $deployments['apps'][$appCode]['current'] = $record;
226
+ $deployments['apps'][$appCode]['history'][] = $record;
227
+ saveDeployments($deployments);
228
+
229
+ appendLog("ROLLBACK app={$appCode} to={$version} prev={$previousVersion}");
230
+
231
+ echo json_encode([
232
+ 'status' => 'SUCCESS',
233
+ 'message' => "Rolled back to version {$version}.",
234
+ 'appCode' => $appCode,
235
+ 'rolledBackTo' => $version,
236
+ 'previousVersion' => $previousVersion,
237
+ 'completedAt' => date(DATE_ATOM),
238
+ ]);
239
+ }
240
+
241
+ // ═══════════════════════════════════════════════════════════════════════════════
242
+ // ── Helpers ───────────────────────────────────────────────────────────────────
243
+ // ═══════════════════════════════════════════════════════════════════════════════
244
+
245
+ function loadDeployments() {
246
+ global $deploymentsFile;
247
+ if (file_exists($deploymentsFile)) {
248
+ $data = json_decode(file_get_contents($deploymentsFile), true);
249
+ if (is_array($data)) return $data;
250
+ }
251
+ return ['apps' => []];
252
+ }
253
+
254
+ function saveDeployments($data) {
255
+ global $deploymentsFile;
256
+ file_put_contents($deploymentsFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
257
+ }
258
+
259
+ function appendLog($message) {
260
+ global $logsDir;
261
+ $logFile = "{$logsDir}/deploy.log";
262
+ $line = date(DATE_ATOM) . " {$message}\n";
263
+ @file_put_contents($logFile, $line, FILE_APPEND);
264
+ }
265
+
266
+ function switchCurrent($currentPath, $releasePath) {
267
+ @mkdir(dirname($currentPath), 0775, true);
268
+ if (is_link($currentPath) || file_exists($currentPath)) {
269
+ if (is_link($currentPath)) {
270
+ @unlink($currentPath);
271
+ } else {
272
+ @rename($currentPath, "{$currentPath}_bak_" . date('Ymd_His'));
273
+ }
274
+ }
275
+ if (!@symlink($releasePath, $currentPath)) {
276
+ recurseCopy($releasePath, $currentPath);
277
+ }
278
+ }
279
+
280
+ function recurseCopy($src, $dst) {
281
+ @mkdir($dst, 0775, true);
282
+ $dir = opendir($src);
283
+ while (($file = readdir($dir)) !== false) {
284
+ if ($file === '.' || $file === '..') continue;
285
+ $srcPath = "{$src}/{$file}";
286
+ $dstPath = "{$dst}/{$file}";
287
+ if (is_dir($srcPath)) {
288
+ recurseCopy($srcPath, $dstPath);
289
+ } else {
290
+ copy($srcPath, $dstPath);
291
+ }
292
+ }
293
+ closedir($dir);
294
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "cloud-ide-cide",
3
+ "version": "2.0.34",
4
+ "description": "Cloud IDE CLI — create, build, publish, upload and deploy Cloud IDE projects.",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "cide": "cli.js"
8
+ },
9
+ "files": [
10
+ "cli.js",
11
+ "cideShell.js",
12
+ "buildProject.js",
13
+ "buildWorkspace.js",
14
+ "buildAllProjects.js",
15
+ "publishPackage.js",
16
+ "createProject.js",
17
+ "startProject.js",
18
+ "watchLinkProject.js",
19
+ "resolveNgProjectName.js",
20
+ "uploadProject.js",
21
+ "serverInit.js",
22
+ "deployer/"
23
+ ],
24
+ "dependencies": {
25
+ "adm-zip": "^0.5.10",
26
+ "axios": "^1.6.2",
27
+ "commander": "^11.1.0",
28
+ "dotenv": "^16.3.1",
29
+ "form-data": "^4.0.0",
30
+ "ts-node-dev": "^2.0.0"
31
+ },
32
+ "scripts": {
33
+ "test": "echo \"Error: no test specified\" && exit 1"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "keywords": [
39
+ "cloudide",
40
+ "cli",
41
+ "deploy",
42
+ "upload",
43
+ "angular",
44
+ "node",
45
+ "react",
46
+ "developer-tools",
47
+ "project-management"
48
+ ],
49
+ "author": "Ankush Shivlal Bhure",
50
+ "license": "ISC",
51
+ "repository": {
52
+ }
53
+ }