moontraze 2.0.6 → 2.0.8
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/lib/deploy.js +150 -23
- package/package.json +1 -1
package/lib/deploy.js
CHANGED
|
@@ -6,6 +6,72 @@ const chalk = require('chalk');
|
|
|
6
6
|
const { getConfig, getProject } = require('./config');
|
|
7
7
|
const { zipCwd } = require('./zip');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Job status poll + Vercel-style gray progress
|
|
11
|
+
*/
|
|
12
|
+
async function waitForDeployJob({ apiUrl, token, jobId }) {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
const timeoutMs = 15 * 60 * 1000; // 15 min
|
|
15
|
+
let lastMessage = '';
|
|
16
|
+
|
|
17
|
+
const printProgress = (msg) => {
|
|
18
|
+
if (!msg || msg === lastMessage) return;
|
|
19
|
+
lastMessage = msg;
|
|
20
|
+
process.stdout.write('\r\x1b[K' + chalk.gray(' ' + msg));
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
printProgress('Queued...');
|
|
24
|
+
|
|
25
|
+
while (Date.now() - start < timeoutMs) {
|
|
26
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
27
|
+
|
|
28
|
+
let data = {};
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(
|
|
31
|
+
`${apiUrl}/api/hosting/deployments/job/${encodeURIComponent(jobId)}`,
|
|
32
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
33
|
+
);
|
|
34
|
+
data = await res.json().catch(() => ({}));
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(data.error || `Status check failed (${res.status})`);
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
printProgress('Checking status...');
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const state = data.state;
|
|
44
|
+
const progress = data.progress;
|
|
45
|
+
let message = 'Working...';
|
|
46
|
+
|
|
47
|
+
if (typeof progress === 'object' && progress?.message) {
|
|
48
|
+
message = progress.message;
|
|
49
|
+
} else if (typeof progress === 'number') {
|
|
50
|
+
message = `Building... ${progress}%`;
|
|
51
|
+
} else if (state === 'waiting' || state === 'delayed') {
|
|
52
|
+
message = 'Queued — waiting for worker...';
|
|
53
|
+
} else if (state === 'active') {
|
|
54
|
+
message = 'Building...';
|
|
55
|
+
} else if (state === 'completed') {
|
|
56
|
+
message = 'Done';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
printProgress(message);
|
|
60
|
+
|
|
61
|
+
if (state === 'completed') {
|
|
62
|
+
process.stdout.write('\n');
|
|
63
|
+
return data.result || {};
|
|
64
|
+
}
|
|
65
|
+
if (state === 'failed') {
|
|
66
|
+
process.stdout.write('\n');
|
|
67
|
+
throw new Error(data.error || 'Deploy failed');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
process.stdout.write('\n');
|
|
72
|
+
throw new Error('Deploy timed out. Check dashboard or try again.');
|
|
73
|
+
}
|
|
74
|
+
|
|
9
75
|
async function deploy({
|
|
10
76
|
prod = true,
|
|
11
77
|
projectName,
|
|
@@ -13,21 +79,19 @@ async function deploy({
|
|
|
13
79
|
domain: domainArg,
|
|
14
80
|
} = {}) {
|
|
15
81
|
const cfg = getConfig();
|
|
16
|
-
|
|
17
82
|
const api =
|
|
18
83
|
apiUrlArg ||
|
|
19
84
|
process.env.MOON_API ||
|
|
20
85
|
cfg.apiUrl ||
|
|
21
86
|
'https://api.moontraze.com';
|
|
22
|
-
|
|
23
87
|
const domain =
|
|
24
88
|
domainArg ||
|
|
25
89
|
process.env.MOON_DOMAIN ||
|
|
26
90
|
cfg.domain ||
|
|
27
91
|
'moontraze.com';
|
|
28
|
-
|
|
29
92
|
const token = cfg.token || process.env.MOON_TOKEN || null;
|
|
30
|
-
const label =
|
|
93
|
+
const label =
|
|
94
|
+
process.env.MOON_LABEL || process.env.MOON_PLATFORM || 'Moontraze';
|
|
31
95
|
|
|
32
96
|
if (!token) {
|
|
33
97
|
throw new Error(
|
|
@@ -38,7 +102,10 @@ async function deploy({
|
|
|
38
102
|
const linked = getProject();
|
|
39
103
|
const name = (projectName || linked?.projectName || '')
|
|
40
104
|
.toLowerCase()
|
|
41
|
-
.
|
|
105
|
+
.trim()
|
|
106
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
107
|
+
.replace(/-+/g, '-')
|
|
108
|
+
.replace(/^-|-$/g, '');
|
|
42
109
|
|
|
43
110
|
if (!name || name.length < 3) {
|
|
44
111
|
throw new Error(
|
|
@@ -50,8 +117,6 @@ async function deploy({
|
|
|
50
117
|
console.log(chalk.cyan(label));
|
|
51
118
|
console.log(` API ${api}`);
|
|
52
119
|
console.log(` Project ${name}`);
|
|
53
|
-
console.log(` Target https://${name}.${domain}`);
|
|
54
|
-
console.log('');
|
|
55
120
|
|
|
56
121
|
const spinner = ora('Packaging...').start();
|
|
57
122
|
const t0 = Date.now();
|
|
@@ -61,22 +126,47 @@ async function deploy({
|
|
|
61
126
|
zipPath = await zipCwd();
|
|
62
127
|
const mb = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(2);
|
|
63
128
|
|
|
129
|
+
// -------- Check if project already exists --------
|
|
64
130
|
spinner.text = 'Checking project...';
|
|
65
131
|
let alreadyExists = false;
|
|
132
|
+
let existingUrl = null;
|
|
133
|
+
let hostLabel = null;
|
|
66
134
|
|
|
67
135
|
try {
|
|
68
|
-
const statusRes = await fetch(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
136
|
+
const statusRes = await fetch(
|
|
137
|
+
`${api}/api/hosting/status/${encodeURIComponent(name)}`,
|
|
138
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
139
|
+
);
|
|
140
|
+
const statusData = await statusRes.json().catch(() => ({}));
|
|
141
|
+
|
|
142
|
+
// exists true = deployed / building / failed (status API fix ke baad)
|
|
143
|
+
if (statusRes.ok && statusData.exists) {
|
|
144
|
+
alreadyExists = true;
|
|
145
|
+
existingUrl = statusData.primaryUrl || statusData.url || null;
|
|
146
|
+
hostLabel = statusData.hostLabel || null;
|
|
74
147
|
}
|
|
75
148
|
} catch (_) {
|
|
76
|
-
//
|
|
149
|
+
// network fail → backend khud decide karega
|
|
77
150
|
}
|
|
78
151
|
|
|
79
|
-
|
|
152
|
+
// Target URL dikhao
|
|
153
|
+
const previewUrl =
|
|
154
|
+
existingUrl ||
|
|
155
|
+
(hostLabel
|
|
156
|
+
? `https://${hostLabel}.${domain}`
|
|
157
|
+
: `https://${name}.${domain}`);
|
|
158
|
+
spinner.stop();
|
|
159
|
+
console.log(` Target ${previewUrl}`);
|
|
160
|
+
console.log(
|
|
161
|
+
chalk.gray(
|
|
162
|
+
alreadyExists
|
|
163
|
+
? ' Mode Redeploy (existing project)'
|
|
164
|
+
: ' Mode New deploy'
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
console.log('');
|
|
168
|
+
|
|
169
|
+
spinner.start(`Uploading (${mb} MB)...`);
|
|
80
170
|
|
|
81
171
|
const form = new FormData();
|
|
82
172
|
form.append('projectName', name);
|
|
@@ -84,12 +174,10 @@ async function deploy({
|
|
|
84
174
|
filename: `${name}.zip`,
|
|
85
175
|
contentType: 'application/zip',
|
|
86
176
|
});
|
|
177
|
+
// Backend bhi force-redeploy karta hai agar same name exist kare
|
|
87
178
|
form.append('redeploy', alreadyExists ? 'true' : 'false');
|
|
88
179
|
|
|
89
|
-
const endpoint =
|
|
90
|
-
? `${api}/api/hosting/redeploy`
|
|
91
|
-
: `${api}/api/hosting/deploy`;
|
|
92
|
-
|
|
180
|
+
const endpoint = `${api}/api/hosting/deploy`;
|
|
93
181
|
const res = await fetch(endpoint, {
|
|
94
182
|
method: 'POST',
|
|
95
183
|
headers: {
|
|
@@ -107,23 +195,62 @@ async function deploy({
|
|
|
107
195
|
data = { error: text };
|
|
108
196
|
}
|
|
109
197
|
|
|
110
|
-
|
|
198
|
+
// ---------- Queued flow ----------
|
|
199
|
+
if (res.status === 202 && data.jobId) {
|
|
200
|
+
spinner.stop();
|
|
201
|
+
console.log(chalk.gray(' Upload done — build starting...'));
|
|
202
|
+
console.log('');
|
|
111
203
|
|
|
204
|
+
const result = await waitForDeployJob({
|
|
205
|
+
apiUrl: api,
|
|
206
|
+
token,
|
|
207
|
+
jobId: data.jobId,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const sec = ((Date.now() - t0) / 1000).toFixed(1);
|
|
211
|
+
const url =
|
|
212
|
+
result?.url ||
|
|
213
|
+
existingUrl ||
|
|
214
|
+
data.url ||
|
|
215
|
+
`https://${name}.${domain}`;
|
|
216
|
+
|
|
217
|
+
const wasRedeploy =
|
|
218
|
+
alreadyExists || data.redeploy === true || result?.redeploy === true;
|
|
219
|
+
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(chalk.green(` Production ${url}`));
|
|
222
|
+
if (result?.version || data.deployment?.version) {
|
|
223
|
+
console.log(
|
|
224
|
+
chalk.gray(
|
|
225
|
+
` Version ${result?.version || data.deployment?.version}`
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
console.log(chalk.gray(` Time ${sec}s`));
|
|
230
|
+
console.log(chalk.green(wasRedeploy ? '✓ Redeployed' : '✓ Deployed'));
|
|
231
|
+
console.log('');
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ---------- Sync fallback ----------
|
|
236
|
+
const sec = ((Date.now() - t0) / 1000).toFixed(1);
|
|
112
237
|
if (!res.ok) {
|
|
113
238
|
spinner.fail(`Failed (${sec}s)`);
|
|
114
239
|
throw new Error(data.error || `HTTP ${res.status}`);
|
|
115
240
|
}
|
|
116
241
|
|
|
117
242
|
spinner.succeed(`Ready in ${sec}s`);
|
|
118
|
-
|
|
119
|
-
const url = data.url || `https://${name}.${domain}`;
|
|
243
|
+
const url = data.url || existingUrl || `https://${name}.${domain}`;
|
|
120
244
|
console.log('');
|
|
121
245
|
console.log(chalk.green(` Production ${url}`));
|
|
122
246
|
if (data.deployment?.version) {
|
|
123
247
|
console.log(chalk.gray(` Version ${data.deployment.version}`));
|
|
124
248
|
}
|
|
125
|
-
console.log(
|
|
249
|
+
console.log(
|
|
250
|
+
chalk.green(alreadyExists || data.redeploy ? '✓ Redeployed' : '✓ Deployed')
|
|
251
|
+
);
|
|
126
252
|
console.log('');
|
|
253
|
+
return data;
|
|
127
254
|
} finally {
|
|
128
255
|
if (zipPath && fs.existsSync(zipPath)) {
|
|
129
256
|
try {
|