entexto-cli 2.0.0 → 2.1.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/docs/fullappdeploy.md +5 -5
- package/lib/commands/deploy.js +2 -2
- package/lib/commands/tunnel.js +96 -37
- package/lib/utils/api.js +5 -5
- package/package.json +1 -1
package/docs/fullappdeploy.md
CHANGED
|
@@ -195,7 +195,7 @@ entexto api query <slug>
|
|
|
195
195
|
|
|
196
196
|
```javascript
|
|
197
197
|
// Autenticación con API Key
|
|
198
|
-
const resp = await fetch('https://api.entexto.com/v1/api/mi-api/
|
|
198
|
+
const resp = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
|
|
199
199
|
method: 'POST',
|
|
200
200
|
headers: {
|
|
201
201
|
'Content-Type': 'application/json',
|
|
@@ -214,7 +214,7 @@ const login = await fetch('https://api.entexto.com/v1/api/auth/login', {
|
|
|
214
214
|
});
|
|
215
215
|
const { token } = await login.json();
|
|
216
216
|
|
|
217
|
-
const docs = await fetch('https://api.entexto.com/v1/api/mi-api/
|
|
217
|
+
const docs = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
|
|
218
218
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
219
219
|
});
|
|
220
220
|
```
|
|
@@ -476,17 +476,17 @@ entexto api insert tienda-api
|
|
|
476
476
|
|
|
477
477
|
Usar desde el frontend:
|
|
478
478
|
```javascript
|
|
479
|
-
const API_BASE = 'https://api.entexto.com/v1/api/tienda-api';
|
|
479
|
+
const API_BASE = 'https://api.entexto.com/v1/api/projects/tienda-api';
|
|
480
480
|
const API_KEY = 'tu-api-key';
|
|
481
481
|
|
|
482
482
|
// Listar productos
|
|
483
|
-
const res = await fetch(`${API_BASE}/
|
|
483
|
+
const res = await fetch(`${API_BASE}/data/productos`, {
|
|
484
484
|
headers: { 'X-API-Key': API_KEY }
|
|
485
485
|
});
|
|
486
486
|
const { documents } = await res.json();
|
|
487
487
|
|
|
488
488
|
// Crear pedido
|
|
489
|
-
await fetch(`${API_BASE}/
|
|
489
|
+
await fetch(`${API_BASE}/data/pedidos`, {
|
|
490
490
|
method: 'POST',
|
|
491
491
|
headers: {
|
|
492
492
|
'Content-Type': 'application/json',
|
package/lib/commands/deploy.js
CHANGED
|
@@ -126,7 +126,7 @@ async function ejecutarDeploy(uuid, dir, publish, full) {
|
|
|
126
126
|
: await deployFiles(uuid, form, false);
|
|
127
127
|
|
|
128
128
|
spinner.stop();
|
|
129
|
-
(result.archivos || []).forEach(r => console.log(' ' + chalk.green('') + ' ' + chalk.white(r)));
|
|
129
|
+
(result.archivos || []).forEach(r => console.log(' ' + chalk.green('') + ' ' + chalk.white(typeof r === 'string' ? r : r.ruta || r)));
|
|
130
130
|
console.log(
|
|
131
131
|
chalk.bold.green(`\n Deploy ${result.total || aSubir.length} subido(s)`)
|
|
132
132
|
+ (omitidos.length ? chalk.gray(`, ${omitidos.length} sin cambios`) : '') + '\n'
|
|
@@ -202,4 +202,4 @@ module.exports = async function (options) {
|
|
|
202
202
|
watcher.on('error', err => console.error(chalk.red(' [watch] Error: ' + err.message)));
|
|
203
203
|
};
|
|
204
204
|
|
|
205
|
-
module.exports.subirArchivoSolo = subirArchivoSolo;
|
|
205
|
+
module.exports.subirArchivoSolo = subirArchivoSolo;
|
package/lib/commands/tunnel.js
CHANGED
|
@@ -102,7 +102,7 @@ module.exports = async function tunnel(opts) {
|
|
|
102
102
|
|
|
103
103
|
// Procesar requests del server
|
|
104
104
|
socket.on('tunnel-request', (payload, cb) => {
|
|
105
|
-
const { method, path: reqPath, headers, body, id } = payload;
|
|
105
|
+
const { method, path: reqPath, headers, body, id, streamId } = payload;
|
|
106
106
|
|
|
107
107
|
// Construir URL completa al target local
|
|
108
108
|
const localUrl = new URL(reqPath, targetUrl);
|
|
@@ -126,55 +126,114 @@ module.exports = async function tunnel(opts) {
|
|
|
126
126
|
const timestamp = new Date().toLocaleTimeString();
|
|
127
127
|
process.stdout.write(chalk.gray(` [${timestamp}] `) + chalk.yellow(method.padEnd(6)) + chalk.white(reqPath) + ' ');
|
|
128
128
|
|
|
129
|
+
// Detectar si el server soporta streaming (envía streamId)
|
|
130
|
+
const useStreaming = !!streamId;
|
|
131
|
+
|
|
129
132
|
const localReq = proto.request(reqOpts, (localRes) => {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
let bodyBuf = Buffer.concat(chunks);
|
|
134
|
-
|
|
135
|
-
// Reescribir el host local → URL pública del túnel en respuestas de texto.
|
|
136
|
-
// Esto cubre el caso de Elementor/webpack cuyo __webpack_public_path__
|
|
137
|
-
// queda hardcodeado como cmy-rent-car.local (o el dominio local) en el JS.
|
|
138
|
-
const ct = (localRes.headers['content-type'] || '').toLowerCase();
|
|
139
|
-
const isText = /\b(javascript|css|html|json|xml|svg|text\/)/i.test(ct);
|
|
140
|
-
if (isText && publicUrl && bodyBuf.length > 0) {
|
|
141
|
-
const localOrigins = [
|
|
142
|
-
parsedTarget.origin, // http://cmy-rent-car.local
|
|
143
|
-
parsedTarget.origin.replace(/^http:/, 'https:'), // https://cmy-rent-car.local
|
|
144
|
-
parsedTarget.host, // cmy-rent-car.local
|
|
145
|
-
];
|
|
146
|
-
let text = bodyBuf.toString('utf8');
|
|
147
|
-
for (const origin of localOrigins) {
|
|
148
|
-
// Reemplazar como origen completo primero, luego como host suelto
|
|
149
|
-
text = text.split(origin).join(publicUrl);
|
|
150
|
-
}
|
|
151
|
-
bodyBuf = Buffer.from(text, 'utf8');
|
|
152
|
-
// Corregir Content-Length si venía fijo
|
|
153
|
-
localRes.headers['content-length'] = String(bodyBuf.length);
|
|
154
|
-
}
|
|
133
|
+
const ct = (localRes.headers['content-type'] || '').toLowerCase();
|
|
134
|
+
const isText = /\b(javascript|css|html|json|xml|svg|text\/)/i.test(ct);
|
|
135
|
+
const status = localRes.statusCode;
|
|
155
136
|
|
|
156
|
-
|
|
157
|
-
|
|
137
|
+
if (useStreaming) {
|
|
138
|
+
// ── Modo streaming: enviar head + chunks + end ──
|
|
158
139
|
const statusColor = status < 300 ? chalk.green(status) : status < 400 ? chalk.cyan(status) : chalk.red(status);
|
|
159
|
-
|
|
140
|
+
process.stdout.write(statusColor + ' ');
|
|
141
|
+
|
|
142
|
+
// Para responses de texto pequeños, acumular para poder reescribir URLs
|
|
143
|
+
// Para binarios o responses grandes, hacer streaming directo
|
|
144
|
+
const isSmallText = isText && (!localRes.headers['content-length'] || parseInt(localRes.headers['content-length']) < 5 * 1024 * 1024);
|
|
145
|
+
|
|
146
|
+
if (isSmallText) {
|
|
147
|
+
// Acumular texto para URL rewriting, luego enviar en un solo stream
|
|
148
|
+
const chunks = [];
|
|
149
|
+
localRes.on('data', (chunk) => chunks.push(chunk));
|
|
150
|
+
localRes.on('end', () => {
|
|
151
|
+
let bodyBuf = Buffer.concat(chunks);
|
|
152
|
+
if (publicUrl && bodyBuf.length > 0) {
|
|
153
|
+
const localOrigins = [
|
|
154
|
+
parsedTarget.origin,
|
|
155
|
+
parsedTarget.origin.replace(/^http:/, 'https:'),
|
|
156
|
+
parsedTarget.host,
|
|
157
|
+
];
|
|
158
|
+
let text = bodyBuf.toString('utf8');
|
|
159
|
+
for (const origin of localOrigins) {
|
|
160
|
+
text = text.split(origin).join(publicUrl);
|
|
161
|
+
}
|
|
162
|
+
bodyBuf = Buffer.from(text, 'utf8');
|
|
163
|
+
}
|
|
164
|
+
const h = { ...localRes.headers };
|
|
165
|
+
h['content-length'] = String(bodyBuf.length);
|
|
166
|
+
socket.emit('tunnel-stream-head', { streamId, status, headers: h });
|
|
167
|
+
socket.emit('tunnel-stream-chunk', { streamId, data: bodyBuf.toString('base64') });
|
|
168
|
+
socket.emit('tunnel-stream-end', { streamId });
|
|
169
|
+
console.log(chalk.gray(`(${bodyBuf.length}B)`));
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
// Streaming real: enviar chunks conforme llegan
|
|
173
|
+
const h = { ...localRes.headers };
|
|
174
|
+
delete h['content-length']; // no sabemos el tamaño final
|
|
175
|
+
socket.emit('tunnel-stream-head', { streamId, status, headers: h });
|
|
176
|
+
|
|
177
|
+
let totalBytes = 0;
|
|
178
|
+
localRes.on('data', (chunk) => {
|
|
179
|
+
totalBytes += chunk.length;
|
|
180
|
+
socket.emit('tunnel-stream-chunk', { streamId, data: chunk.toString('base64') });
|
|
181
|
+
});
|
|
182
|
+
localRes.on('end', () => {
|
|
183
|
+
socket.emit('tunnel-stream-end', { streamId });
|
|
184
|
+
console.log(chalk.gray(`(${totalBytes}B streamed)`));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
// ── Modo clásico: acumular todo y callback ──
|
|
189
|
+
const chunks = [];
|
|
190
|
+
localRes.on('data', (chunk) => chunks.push(chunk));
|
|
191
|
+
localRes.on('end', () => {
|
|
192
|
+
let bodyBuf = Buffer.concat(chunks);
|
|
193
|
+
|
|
194
|
+
if (isText && publicUrl && bodyBuf.length > 0) {
|
|
195
|
+
const localOrigins = [
|
|
196
|
+
parsedTarget.origin,
|
|
197
|
+
parsedTarget.origin.replace(/^http:/, 'https:'),
|
|
198
|
+
parsedTarget.host,
|
|
199
|
+
];
|
|
200
|
+
let text = bodyBuf.toString('utf8');
|
|
201
|
+
for (const origin of localOrigins) {
|
|
202
|
+
text = text.split(origin).join(publicUrl);
|
|
203
|
+
}
|
|
204
|
+
bodyBuf = Buffer.from(text, 'utf8');
|
|
205
|
+
localRes.headers['content-length'] = String(bodyBuf.length);
|
|
206
|
+
}
|
|
160
207
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
208
|
+
const statusColor = status < 300 ? chalk.green(status) : status < 400 ? chalk.cyan(status) : chalk.red(status);
|
|
209
|
+
console.log(statusColor + chalk.gray(` (${bodyBuf.length}B)`));
|
|
210
|
+
|
|
211
|
+
cb({
|
|
212
|
+
status: status,
|
|
213
|
+
headers: localRes.headers,
|
|
214
|
+
body: bodyBuf.toString('base64'),
|
|
215
|
+
});
|
|
165
216
|
});
|
|
166
|
-
}
|
|
217
|
+
}
|
|
167
218
|
});
|
|
168
219
|
|
|
169
220
|
localReq.on('error', (err) => {
|
|
170
221
|
console.log(chalk.red('ERR ') + chalk.gray(err.message));
|
|
171
|
-
|
|
222
|
+
if (useStreaming) {
|
|
223
|
+
socket.emit('tunnel-stream-error', { streamId, error: err.message, status: 502 });
|
|
224
|
+
} else {
|
|
225
|
+
cb({ error: err.message, status: 502 });
|
|
226
|
+
}
|
|
172
227
|
});
|
|
173
228
|
|
|
174
|
-
localReq.setTimeout(25000, () => {
|
|
229
|
+
localReq.setTimeout(useStreaming ? 120000 : 25000, () => {
|
|
175
230
|
localReq.destroy();
|
|
176
231
|
console.log(chalk.red('TIMEOUT'));
|
|
177
|
-
|
|
232
|
+
if (useStreaming) {
|
|
233
|
+
socket.emit('tunnel-stream-error', { streamId, error: 'Local server timeout', status: 504 });
|
|
234
|
+
} else {
|
|
235
|
+
cb({ error: 'Local server timeout', status: 504 });
|
|
236
|
+
}
|
|
178
237
|
});
|
|
179
238
|
|
|
180
239
|
if (body) {
|
package/lib/utils/api.js
CHANGED
|
@@ -104,16 +104,16 @@ async function deleteFileRemote(uuid, ruta) {
|
|
|
104
104
|
// ─── Projects CRUD ─────────────────────────────────────────
|
|
105
105
|
async function createProject(data) {
|
|
106
106
|
const res = await client().post('/api/projects', data);
|
|
107
|
-
return res.data;
|
|
107
|
+
return res.data.project || res.data;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// ─── Dominios ──────────────────────────────────────────────
|
|
111
111
|
async function listDomains() {
|
|
112
112
|
const res = await client().get('/api/domains/list');
|
|
113
|
-
return res.data;
|
|
113
|
+
return res.data.domains || res.data || [];
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async function addDomain(
|
|
116
|
+
async function addDomain(projectId, domain) {
|
|
117
117
|
const res = await client().post('/api/domains/add', { domain, project_id: projectId });
|
|
118
118
|
return res.data;
|
|
119
119
|
}
|
|
@@ -158,7 +158,7 @@ async function apiCreateCollection(slug, name) {
|
|
|
158
158
|
|
|
159
159
|
async function apiInsertDoc(slug, collection, data) {
|
|
160
160
|
const res = await client().post(
|
|
161
|
-
`/v1/api/projects/${encodeURIComponent(slug)}/
|
|
161
|
+
`/v1/api/projects/${encodeURIComponent(slug)}/data/${encodeURIComponent(collection)}`,
|
|
162
162
|
{ data }
|
|
163
163
|
);
|
|
164
164
|
return res.data;
|
|
@@ -166,7 +166,7 @@ async function apiInsertDoc(slug, collection, data) {
|
|
|
166
166
|
|
|
167
167
|
async function apiQueryDocs(slug, collection) {
|
|
168
168
|
const res = await client().get(
|
|
169
|
-
`/v1/api/projects/${encodeURIComponent(slug)}/
|
|
169
|
+
`/v1/api/projects/${encodeURIComponent(slug)}/data/${encodeURIComponent(collection)}`
|
|
170
170
|
);
|
|
171
171
|
return res.data;
|
|
172
172
|
}
|