entexto-cli 1.3.0 → 1.4.1
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/commands/sync.js +63 -1
- package/lib/utils/api.js +7 -2
- package/package.json +1 -1
package/lib/commands/sync.js
CHANGED
|
@@ -8,7 +8,7 @@ const ora = require('ora');
|
|
|
8
8
|
const FormData = require('form-data');
|
|
9
9
|
const chokidar = require('chokidar');
|
|
10
10
|
const { getToken } = require('../utils/config');
|
|
11
|
-
const { getProjects, deployFiles, getManifest, pullProject, streamEvents } = require('../utils/api');
|
|
11
|
+
const { getProjects, deployFiles, getManifest, pullProject, streamEvents, deleteFileRemote } = require('../utils/api');
|
|
12
12
|
const { hashFile, hashContent } = require('../utils/delta');
|
|
13
13
|
|
|
14
14
|
const IGNORE_NAMES = new Set([
|
|
@@ -43,6 +43,7 @@ function recolectarArchivos(dir) {
|
|
|
43
43
|
|
|
44
44
|
// Rastrea cuándo subimos cada archivo (para evitar eco del SSE)
|
|
45
45
|
const localUploadedAt = new Map();
|
|
46
|
+
const localDeletedAt = new Map();
|
|
46
47
|
|
|
47
48
|
async function subirArchivoSolo(uuid, absDir, filePath) {
|
|
48
49
|
if (!fs.existsSync(filePath)) return;
|
|
@@ -60,6 +61,19 @@ async function subirArchivoSolo(uuid, absDir, filePath) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
async function eliminarArchivoRemoto(uuid, absDir, filePath) {
|
|
65
|
+
const ruta = path.relative(absDir, filePath).replace(/\\/g, '/');
|
|
66
|
+
if (debeIgnorar(path.basename(filePath))) return;
|
|
67
|
+
try {
|
|
68
|
+
await deleteFileRemote(uuid, ruta);
|
|
69
|
+
localDeletedAt.set(ruta, Date.now());
|
|
70
|
+
console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' borrado → remoto'));
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const msg = err.response?.data?.error || err.message;
|
|
73
|
+
console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.red(' error borrando: ' + msg));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
63
77
|
module.exports = async function sync(options) {
|
|
64
78
|
if (!getToken()) {
|
|
65
79
|
console.error(chalk.red('✖ No has iniciado sesión. Ejecuta: entexto login'));
|
|
@@ -218,6 +232,36 @@ module.exports = async function sync(options) {
|
|
|
218
232
|
let sseReconnectTimer = null;
|
|
219
233
|
|
|
220
234
|
function manejarEventoRemoto(eventType, data) {
|
|
235
|
+
if (eventType === 'file-delete') {
|
|
236
|
+
const { ruta } = data || {};
|
|
237
|
+
if (!ruta) return;
|
|
238
|
+
const destFile = path.join(absDir, ruta);
|
|
239
|
+
try {
|
|
240
|
+
if (fs.existsSync(destFile)) {
|
|
241
|
+
fs.unlinkSync(destFile);
|
|
242
|
+
localDeletedAt.set(ruta, Date.now());
|
|
243
|
+
console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' ← borrado remoto'));
|
|
244
|
+
}
|
|
245
|
+
} catch { console.log(' ' + chalk.red('✖') + ' No se pudo borrar: ' + ruta); }
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (eventType === 'file-rename') {
|
|
249
|
+
const { ruta_antigua, ruta_nueva } = data || {};
|
|
250
|
+
if (!ruta_antigua || !ruta_nueva) return;
|
|
251
|
+
const oldFile = path.join(absDir, ruta_antigua);
|
|
252
|
+
const newFile = path.join(absDir, ruta_nueva);
|
|
253
|
+
const newDir = path.dirname(newFile);
|
|
254
|
+
try {
|
|
255
|
+
if (fs.existsSync(oldFile)) {
|
|
256
|
+
if (!fs.existsSync(newDir)) fs.mkdirSync(newDir, { recursive: true });
|
|
257
|
+
fs.renameSync(oldFile, newFile);
|
|
258
|
+
localDeletedAt.set(ruta_antigua, Date.now());
|
|
259
|
+
localUploadedAt.set(ruta_nueva, Date.now());
|
|
260
|
+
console.log(' ' + chalk.yellow('↔') + ' ' + chalk.white(ruta_antigua) + chalk.gray(' → ') + chalk.white(ruta_nueva));
|
|
261
|
+
}
|
|
262
|
+
} catch { console.log(' ' + chalk.red('✖') + ' No se pudo renombrar: ' + ruta_antigua); }
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
221
265
|
if (eventType !== 'file-update') return;
|
|
222
266
|
const { ruta, contenido } = data || {};
|
|
223
267
|
if (!ruta || contenido === undefined) return;
|
|
@@ -304,8 +348,26 @@ module.exports = async function sync(options) {
|
|
|
304
348
|
watcher.on('change', fp => { changed.add(fp); disparar(); });
|
|
305
349
|
watcher.on('error', err => console.error(chalk.red(' [watch] Error: ' + err.message)));
|
|
306
350
|
|
|
351
|
+
const deletedFiles = new Set();
|
|
352
|
+
let deleteDebounce = null;
|
|
353
|
+
|
|
354
|
+
watcher.on('unlink', fp => {
|
|
355
|
+
const ruta = path.relative(absDir, fp).replace(/\\/g, '/');
|
|
356
|
+
if (debeIgnorar(path.basename(fp))) return;
|
|
357
|
+
const deletedAt = localDeletedAt.get(ruta);
|
|
358
|
+
if (deletedAt && Date.now() - deletedAt < 3000) return;
|
|
359
|
+
deletedFiles.add(fp);
|
|
360
|
+
clearTimeout(deleteDebounce);
|
|
361
|
+
deleteDebounce = setTimeout(async () => {
|
|
362
|
+
const files = [...deletedFiles];
|
|
363
|
+
deletedFiles.clear();
|
|
364
|
+
for (const f of files) await eliminarArchivoRemoto(uuid, absDir, f);
|
|
365
|
+
}, 300);
|
|
366
|
+
});
|
|
367
|
+
|
|
307
368
|
process.on('SIGINT', () => {
|
|
308
369
|
clearTimeout(sseReconnectTimer);
|
|
370
|
+
clearTimeout(deleteDebounce);
|
|
309
371
|
if (sseStream) try { sseStream.destroy(); } catch {}
|
|
310
372
|
watcher.close();
|
|
311
373
|
console.log(chalk.gray('\n Sync detenido.\n'));
|
package/lib/utils/api.js
CHANGED
|
@@ -79,11 +79,11 @@ async function streamEvents(uuid, onEvent) {
|
|
|
79
79
|
});
|
|
80
80
|
const stream = response.data;
|
|
81
81
|
let buffer = '';
|
|
82
|
+
let eventType = 'message', dataStr = '';
|
|
82
83
|
stream.on('data', (chunk) => {
|
|
83
84
|
buffer += chunk.toString();
|
|
84
85
|
const parts = buffer.split('\n');
|
|
85
86
|
buffer = parts.pop();
|
|
86
|
-
let eventType = 'message', dataStr = '';
|
|
87
87
|
for (const line of parts) {
|
|
88
88
|
if (line.startsWith('event: ')) eventType = line.slice(7).trim();
|
|
89
89
|
else if (line.startsWith('data: ')) dataStr = line.slice(6).trim();
|
|
@@ -96,4 +96,9 @@ async function streamEvents(uuid, onEvent) {
|
|
|
96
96
|
return stream;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
async function deleteFileRemote(uuid, ruta) {
|
|
100
|
+
const res = await client().post(`/api/cli/delete/${uuid}`, { ruta });
|
|
101
|
+
return res.data;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { login, getProjects, deployFiles, deployWithPublish, publishProject, pullProject, getManifest, streamEvents, deleteFileRemote };
|