mgpanel-cli 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/bin/mgpanel.js +514 -7
  2. package/package.json +1 -1
package/bin/mgpanel.js CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const http = require("http");
4
+ const https = require("https");
4
5
  const fs = require("fs");
5
6
  const path = require("path");
7
+ const { URL } = require("url");
6
8
 
7
9
  function printHelp() {
8
10
  console.log(`
@@ -18,6 +20,8 @@ Uso:
18
20
 
19
21
  mgpanel dev [puerto]
20
22
 
23
+ mgpanel publish --account <cuenta> [--token <token>] [--api-url <url>]
24
+
21
25
  Ejemplos:
22
26
  mgpanel init miweb
23
27
  cd miweb
@@ -28,6 +32,9 @@ Ejemplos:
28
32
 
29
33
  mgpanel dev
30
34
  mgpanel dev 8080
35
+
36
+ mgpanel publish --account zzz-eloymanuel --token mi-token
37
+ mgpanel publish --account zzz-eloymanuel --token mi-token --api-url https://api.mgpanel.com
31
38
  `);
32
39
  }
33
40
 
@@ -106,6 +113,28 @@ console.log("[MGPanel] Global JS loaded");
106
113
  `;
107
114
  fs.writeFileSync(path.join(projectPath, "index.html"), indexHTML, "utf8");
108
115
 
116
+ // .env: Variables de entorno para publish
117
+ const envContent = `# MGPanel CLI - Variables de entorno
118
+ # Descomenta y configura estas variables para usar 'mgpanel publish'
119
+
120
+ # Token de autenticación para publicar (obtén uno desde el panel de MGPanel)
121
+ # MGPANEL_TOKEN=tu-token-aqui
122
+
123
+ # URL de la API (opcional, por defecto usa https://dev.mgpanel.co)
124
+ # MGPANEL_API_URL=https://dev.mgpanel.co
125
+ `;
126
+ fs.writeFileSync(path.join(projectPath, ".env"), envContent, "utf8");
127
+ // Agregar .env al .gitignore si existe
128
+ const gitignorePath = path.join(projectPath, ".gitignore");
129
+ if (fs.existsSync(gitignorePath)) {
130
+ const gitignore = fs.readFileSync(gitignorePath, "utf8");
131
+ if (!gitignore.includes(".env")) {
132
+ fs.appendFileSync(gitignorePath, "\n# MGPanel CLI\n.env\n");
133
+ }
134
+ } else {
135
+ fs.writeFileSync(gitignorePath, "# MGPanel CLI\n.env\n", "utf8");
136
+ }
137
+
109
138
  // dev/preview.js: carga una page leyendo page.json y componiendo módulos
110
139
  const previewJS = `async function loadModule(name) {
111
140
  const base = \`modules/\${name}/\${name}\`;
@@ -185,6 +214,7 @@ renderPage(pageFromPath()).catch(err => {
185
214
  route: "/",
186
215
  title: "Home",
187
216
  description: "Página principal creada con MGPanel.",
217
+ directory: "",
188
218
  modules: []
189
219
  };
190
220
  fs.writeFileSync(
@@ -248,10 +278,23 @@ Esto crea:
248
278
  "route": "/about",
249
279
  "title": "About",
250
280
  "description": "Descripción de la página about.",
251
- "modules": []
281
+ "directory": "",
282
+ "modules": [
283
+ { "name": "header", "import": false }
284
+ ]
252
285
  }
253
286
  \`\`\`
254
287
 
288
+ **Campo \`directory\`:**
289
+ - Por defecto viene vacío (\`""\`)
290
+ - Puede editarse manualmente para agregar valores como \`"app/"\` cuando se requiera
291
+ - Se usa en la nube para organizar las páginas en directorios
292
+
293
+ **Campo \`import\` en módulos:**
294
+ - \`import: false\` - Módulo propio de esta página (se crea la sección completa)
295
+ - \`import: true\` - Módulo importado de otra página (se referencia la sección existente)
296
+ - Se asigna automáticamente al agregar módulos con \`mgpanel add module\`
297
+
255
298
  ### Crear un Módulo
256
299
 
257
300
  \`\`\`bash
@@ -290,15 +333,20 @@ Esto actualiza el archivo \`pages/<nombre-pagina>/page.json\` añadiendo el mód
290
333
  "route": "/",
291
334
  "title": "Home",
292
335
  "description": "Página principal.",
336
+ "directory": "",
293
337
  "modules": [
294
- { "name": "header" },
295
- { "name": "hero-banner" },
296
- { "name": "footer" }
338
+ { "name": "header", "import": false },
339
+ { "name": "hero-banner", "import": false },
340
+ { "name": "footer", "import": true }
297
341
  ]
298
342
  }
299
343
  \`\`\`
300
344
 
301
- **Importante:** Los módulos se renderizan en el orden en que aparecen en el array \`modules\`.
345
+ **Importante:**
346
+ - Los módulos se renderizan en el orden en que aparecen en el array \`modules\`.
347
+ - El campo \`import\` se agrega automáticamente:
348
+ - \`import: false\` si el módulo es propio de esta página (primera vez que se agrega)
349
+ - \`import: true\` si el módulo pertenece a otra página y se está reutilizando
302
350
 
303
351
  ### Servidor de Desarrollo
304
352
 
@@ -425,6 +473,26 @@ Por eso, en los módulos con JS, sigue estas reglas para que funcionen *tanto lo
425
473
  •⁠ ⁠*No ocultar contenido sin JS*:
426
474
  - Si usas animaciones por clase (ej. ⁠ \`data-mg-animate\` ⁠), asegúrate que el contenido no quede invisible si el JS no corre.
427
475
 
476
+ ### Campo \`import\` en módulos
477
+
478
+ Cada módulo en \`page.json\` puede tener un campo \`import\` que indica si el módulo es propio de la página o se está importando de otra:
479
+
480
+ - **\`import: false\`** - Módulo propio: La primera página donde se agrega un módulo tiene \`import: false\`. En la nube, esto crea una nueva sección completa.
481
+ - **\`import: true\`** - Módulo importado: Si un módulo ya existe en otra página y lo agregas a una nueva página, tendrá \`import: true\`. En la nube, esto referencia la sección existente en lugar de duplicarla.
482
+
483
+ **Ejemplo:**
484
+ \`\`\`json
485
+ {
486
+ "modules": [
487
+ { "name": "header", "import": false }, // Propio de esta página
488
+ { "name": "banner", "import": false }, // Propio de esta página
489
+ { "name": "footer", "import": true } // Importado de otra página
490
+ ]
491
+ }
492
+ \`\`\`
493
+
494
+ El campo \`import\` se asigna automáticamente cuando usas \`mgpanel add module\`. La primera página donde agregas un módulo será la "dueña" (\`import: false\`), y las páginas subsecuentes que usen ese módulo tendrán \`import: true\`.
495
+
428
496
  #### Plantilla recomendada para JS de módulos (cloud-safe)
429
497
 
430
498
  \`\`\`js
@@ -590,6 +658,7 @@ function cmdMakePage(pageName) {
590
658
  route: `/${pageName === "home" ? "" : pageName}`.replace(/\/$/, "/"),
591
659
  title: pageName.charAt(0).toUpperCase() + pageName.slice(1),
592
660
  description: `Descripción de la página ${pageName}.`,
661
+ directory: "",
593
662
  modules: []
594
663
  },
595
664
  null,
@@ -705,6 +774,35 @@ function cmdMakeModule(moduleName) {
705
774
  if (skipped.length) console.log("↩️ Ya existían:", skipped.join(", "));
706
775
  }
707
776
 
777
+ /**
778
+ * Verifica si un módulo existe en otras páginas (diferentes a la actual)
779
+ */
780
+ function moduleExistsInOtherPages(moduleName, currentPageName) {
781
+ const pagesDir = path.join(process.cwd(), "pages");
782
+ if (!fs.existsSync(pagesDir)) return false;
783
+
784
+ const pageDirs = fs.readdirSync(pagesDir, { withFileTypes: true })
785
+ .filter(dirent => dirent.isDirectory() && dirent.name !== currentPageName)
786
+ .map(dirent => dirent.name);
787
+
788
+ for (const pageName of pageDirs) {
789
+ const pageJsonPath = path.join(pagesDir, pageName, "page.json");
790
+ if (fs.existsSync(pageJsonPath)) {
791
+ try {
792
+ const page = readJSON(pageJsonPath);
793
+ if (Array.isArray(page.modules)) {
794
+ const exists = page.modules.some(m => m && m.name === moduleName);
795
+ if (exists) return true;
796
+ }
797
+ } catch (e) {
798
+ // Si hay error leyendo el archivo, continuar con la siguiente página
799
+ continue;
800
+ }
801
+ }
802
+ }
803
+ return false;
804
+ }
805
+
708
806
  /**
709
807
  * mgpanel add module <moduleName> --to <pageName>
710
808
  */
@@ -738,10 +836,402 @@ function cmdAddModule(moduleName, pageName) {
738
836
  return;
739
837
  }
740
838
 
741
- page.modules.push({ name: moduleName });
839
+ // Determinar si el módulo es importado (existe en otras páginas)
840
+ const isImported = moduleExistsInOtherPages(moduleName, pageName);
841
+ page.modules.push({
842
+ name: moduleName,
843
+ import: isImported
844
+ });
742
845
  writeJSON(pageJsonPath, page);
743
846
 
744
- console.log(`✅ Módulo "${moduleName}" agregado a pages/${pageName}/page.json`);
847
+ const importStatus = isImported ? "importado" : "propio";
848
+ console.log(`✅ Módulo "${moduleName}" agregado a pages/${pageName}/page.json (${importStatus})`);
849
+ }
850
+
851
+ /**
852
+ * mgpanel publish --account <cuenta> [--token <token>] [--api-url <url>]
853
+ */
854
+ async function cmdPublish(args) {
855
+ // Parsear argumentos
856
+ let account = null;
857
+ let token = null;
858
+ let apiUrl = "https://dev.mgpanel.co"; // default
859
+
860
+ for (let i = 0; i < args.length; i++) {
861
+ if (args[i] === "--account" && args[i + 1]) {
862
+ account = args[i + 1];
863
+ i++;
864
+ } else if (args[i] === "--token" && args[i + 1]) {
865
+ token = args[i + 1];
866
+ i++;
867
+ } else if (args[i] === "--api-url" && args[i + 1]) {
868
+ apiUrl = args[i + 1];
869
+ i++;
870
+ }
871
+ }
872
+
873
+ // Cargar variables de entorno desde .env si existe
874
+ const envPath = path.join(process.cwd(), ".env");
875
+ if (fs.existsSync(envPath)) {
876
+ const envContent = fs.readFileSync(envPath, "utf8");
877
+ const envLines = envContent.split("\n");
878
+ for (const line of envLines) {
879
+ const trimmed = line.trim();
880
+ // Ignorar comentarios y líneas vacías
881
+ if (trimmed && !trimmed.startsWith("#")) {
882
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
883
+ if (match) {
884
+ const key = match[1].trim();
885
+ const value = match[2].trim().replace(/^["']|["']$/g, ""); // Remover comillas
886
+ if (key === "MGPANEL_TOKEN") {
887
+ process.env.MGPANEL_TOKEN = value;
888
+ } else if (key === "MGPANEL_API_URL") {
889
+ process.env.MGPANEL_API_URL = value;
890
+ }
891
+ }
892
+ }
893
+ }
894
+ }
895
+
896
+ // Token puede venir de variable de entorno
897
+ if (!token) {
898
+ token = process.env.MGPANEL_TOKEN;
899
+ }
900
+
901
+ // API URL puede venir de variable de entorno (tiene prioridad sobre default)
902
+ if (process.env.MGPANEL_API_URL) {
903
+ apiUrl = process.env.MGPANEL_API_URL;
904
+ }
905
+
906
+ // Validaciones
907
+ if (!account) {
908
+ console.log("❌ Falta el parámetro --account");
909
+ console.log("👉 Usa: mgpanel publish --account <nombre-cuenta> [--token <token>] [--api-url <url>]");
910
+ process.exit(1);
911
+ }
912
+
913
+ if (!token) {
914
+ console.log("❌ Falta el token de autenticación");
915
+ console.log("👉 Usa: mgpanel publish --account <cuenta> --token <token>");
916
+ console.log(" O configura la variable de entorno: MGPANEL_TOKEN");
917
+ console.log(" O agrega MGPANEL_TOKEN=tu-token en el archivo .env");
918
+ process.exit(1);
919
+ }
920
+
921
+ // Mostrar información del token (solo primeros y últimos caracteres por seguridad)
922
+ const tokenPreview = token.length > 10
923
+ ? `${token.substring(0, 6)}...${token.substring(token.length - 4)}`
924
+ : "***";
925
+ console.log(`🔑 Token: ${tokenPreview}`);
926
+
927
+ const projectRoot = process.cwd();
928
+ const pagesDir = path.join(projectRoot, "pages");
929
+ const modulesDir = path.join(projectRoot, "modules");
930
+ const assetsDir = path.join(projectRoot, "assets");
931
+
932
+ // Validar que estamos en un proyecto MGPanel
933
+ if (!fs.existsSync(pagesDir) || !fs.existsSync(modulesDir)) {
934
+ console.log("❌ No se encontró un proyecto MGPanel válido");
935
+ console.log("👉 Asegúrate de estar en un directorio con carpetas 'pages' y 'modules'");
936
+ console.log(" O ejecuta: mgpanel init <nombre-proyecto>");
937
+ process.exit(1);
938
+ }
939
+
940
+ console.log("📦 Leyendo estructura del proyecto...");
941
+
942
+ try {
943
+ // Leer assets globales
944
+ const globalCSS = fs.existsSync(path.join(assetsDir, "css", "style.css"))
945
+ ? fs.readFileSync(path.join(assetsDir, "css", "style.css"), "utf8")
946
+ : "";
947
+ const globalJS = fs.existsSync(path.join(assetsDir, "js", "app.js"))
948
+ ? fs.readFileSync(path.join(assetsDir, "js", "app.js"), "utf8")
949
+ : "";
950
+
951
+ // Leer todas las páginas
952
+ const pages = [];
953
+ const pageDirs = fs.readdirSync(pagesDir, { withFileTypes: true })
954
+ .filter(dirent => dirent.isDirectory())
955
+ .map(dirent => dirent.name);
956
+
957
+ console.log(`📂 Páginas encontradas en directorio: ${pageDirs.length} (${pageDirs.join(", ")})`);
958
+
959
+ for (const pageName of pageDirs) {
960
+ console.log(`📄 Procesando página: ${pageName}`);
961
+ // Compatibilidad: algunas personas lo renombraron a "pago.json" por error.
962
+ // Preferimos "page.json", pero aceptamos "pago.json" como fallback.
963
+ const pageJsonPath = path.join(pagesDir, pageName, "page.json");
964
+ const pagoJsonPath = path.join(pagesDir, pageName, "pago.json");
965
+ let pageConfigPath = null;
966
+
967
+ if (fs.existsSync(pageJsonPath)) {
968
+ pageConfigPath = pageJsonPath;
969
+ } else if (fs.existsSync(pagoJsonPath)) {
970
+ pageConfigPath = pagoJsonPath;
971
+ console.log(`⚠️ Aviso: usando "${pageName}/pago.json". Recomendado: renómbralo a "page.json".`);
972
+ } else {
973
+ console.log(`⚠️ Saltando página "${pageName}": no se encontró page.json (ni pago.json)`);
974
+ continue;
975
+ }
976
+
977
+ let pageConfig;
978
+ try {
979
+ pageConfig = readJSON(pageConfigPath);
980
+ } catch (error) {
981
+ console.log(`❌ Error al leer ${pageConfigPath}: ${error.message}`);
982
+ console.log(`⚠️ Saltando página "${pageName}"`);
983
+ continue;
984
+ }
985
+
986
+ const sections = [];
987
+
988
+ // Procesar módulos de la página
989
+ if (Array.isArray(pageConfig.modules)) {
990
+ for (let i = 0; i < pageConfig.modules.length; i++) {
991
+ const moduleName = pageConfig.modules[i].name;
992
+ if (!moduleName) continue;
993
+
994
+ const moduleDir = path.join(modulesDir, moduleName);
995
+ if (!fs.existsSync(moduleDir)) {
996
+ console.log(`⚠️ Módulo "${moduleName}" no encontrado, saltando...`);
997
+ continue;
998
+ }
999
+
1000
+ const moduleHTML = fs.existsSync(path.join(moduleDir, `${moduleName}.html`))
1001
+ ? fs.readFileSync(path.join(moduleDir, `${moduleName}.html`), "utf8")
1002
+ : "";
1003
+ const moduleCSS = fs.existsSync(path.join(moduleDir, `${moduleName}.css`))
1004
+ ? fs.readFileSync(path.join(moduleDir, `${moduleName}.css`), "utf8")
1005
+ : "";
1006
+ const moduleJS = fs.existsSync(path.join(moduleDir, `${moduleName}.js`))
1007
+ ? fs.readFileSync(path.join(moduleDir, `${moduleName}.js`), "utf8")
1008
+ : "";
1009
+
1010
+ // Leer el campo import del page.json (por defecto false si no existe)
1011
+ const moduleConfig = pageConfig.modules[i];
1012
+ const isImported = moduleConfig.import === true || moduleConfig.import === "true";
1013
+
1014
+ sections.push({
1015
+ code: moduleName,
1016
+ name: moduleName,
1017
+ html: {
1018
+ type: "text/html",
1019
+ code: moduleHTML
1020
+ },
1021
+ css: {
1022
+ type: "text/css",
1023
+ code: moduleCSS
1024
+ },
1025
+ javascript: {
1026
+ type: "application/javascript",
1027
+ code: moduleJS
1028
+ },
1029
+ module: [], // Array de referencias a módulos (el backend lo manejará)
1030
+ order: i + 1,
1031
+ status: 1,
1032
+ import: isImported
1033
+ });
1034
+ }
1035
+ }
1036
+
1037
+ // Validar que la página tenga al menos un campo requerido
1038
+ if (!pageConfig.title && !pageName) {
1039
+ console.log(`⚠️ Saltando página "${pageName}": falta información básica`);
1040
+ continue;
1041
+ }
1042
+
1043
+ pages.push({
1044
+ // page: ObjectId - El backend debe generar o usar el existente
1045
+ name: pageName,
1046
+ title: pageConfig.title || pageName,
1047
+ description: pageConfig.description || "",
1048
+ // IMPORTANTE: en MGPanel el `directory` por defecto debe ser "".
1049
+ // No usar `|| pageName` porque "" es válido y si cae al fallback rompe el render (termina guardando "home", "about", etc.).
1050
+ directory: (typeof pageConfig.directory === "string") ? pageConfig.directory : "",
1051
+ url: pageConfig.route || `/${pageName === "home" ? "" : pageName}`,
1052
+ sections: sections,
1053
+ status: 1,
1054
+ soon: false,
1055
+ index: pageName === "home"
1056
+ });
1057
+
1058
+ console.log(`✅ Página "${pageName}" procesada (${sections.length} secciones)`);
1059
+ }
1060
+
1061
+ console.log(`📊 Total de páginas procesadas: ${pages.length}`);
1062
+
1063
+ // Seguridad: nunca publicar si no se encontró ninguna página.
1064
+ if (pages.length === 0) {
1065
+ console.log("❌ No se encontró ninguna página para publicar.");
1066
+ console.log("👉 Verifica que existan carpetas en ./pages/<pagina>/ y que el archivo sea page.json (o pago.json).");
1067
+ console.log(`💡 Directorios encontrados: ${pageDirs.length > 0 ? pageDirs.join(", ") : "ninguno"}`);
1068
+ process.exit(1);
1069
+ }
1070
+
1071
+ // Construir payload según modelo MongoDB
1072
+ const payload = {
1073
+ type: "published",
1074
+ css: {
1075
+ type: "text/css",
1076
+ code: globalCSS
1077
+ },
1078
+ javascript: {
1079
+ type: "application/javascript",
1080
+ code: globalJS
1081
+ },
1082
+ pages: pages,
1083
+ modified_date: new Date(),
1084
+ has_changes: true,
1085
+ version_number: 1
1086
+ };
1087
+
1088
+ console.log(`📤 Publicando a cuenta: ${account}`);
1089
+ console.log(`📄 Páginas encontradas: ${pages.length}`);
1090
+ console.log(`🧩 Módulos totales: ${pages.reduce((sum, p) => sum + p.sections.length, 0)}`);
1091
+ console.log(`🌐 URL de la API: ${apiUrl}`);
1092
+
1093
+ // Enviar POST request
1094
+ const url = new URL(`${apiUrl}/api/cli/publish`);
1095
+ const postData = JSON.stringify(payload);
1096
+ const urlOptions = {
1097
+ hostname: url.hostname,
1098
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
1099
+ path: url.pathname + url.search,
1100
+ method: "POST",
1101
+ headers: {
1102
+ "Content-Type": "application/json",
1103
+ "Content-Length": Buffer.byteLength(postData),
1104
+ "Authorization": `Bearer ${token}`,
1105
+ "X-Account-Nick": account
1106
+ },
1107
+ timeout: 60000, // 60 segundos de timeout
1108
+ rejectUnauthorized: true, // Aceptar certificados SSL válidos
1109
+ agent: false // No usar agent pool, crear nueva conexión
1110
+ };
1111
+
1112
+ const client = url.protocol === "https:" ? https : http;
1113
+
1114
+ console.log(`🔄 Enviando petición a ${url.hostname}${url.pathname}...`);
1115
+ console.log(`📤 Tamaño del payload: ${Buffer.byteLength(postData)} bytes`);
1116
+
1117
+ // Usar promesa para asegurar que esperamos la respuesta
1118
+ await new Promise((resolve, reject) => {
1119
+ console.log(`🔧 Creando request HTTP...`);
1120
+ const req = client.request(urlOptions, (res) => {
1121
+ console.log(`✅ Callback del request ejecutado`);
1122
+ let responseData = "";
1123
+
1124
+ console.log(`📡 Respuesta recibida: ${res.statusCode} ${res.statusMessage || ""}`);
1125
+
1126
+ // Manejar errores de respuesta
1127
+ res.on("error", (err) => {
1128
+ console.log(`❌ Error al leer la respuesta: ${err.message}`);
1129
+ reject(err);
1130
+ });
1131
+
1132
+ res.on("data", (chunk) => {
1133
+ responseData += chunk;
1134
+ console.log(`📥 Recibiendo datos... (${responseData.length} bytes acumulados)`);
1135
+ });
1136
+
1137
+ res.on("end", () => {
1138
+ console.log(`✅ Evento 'end' disparado`);
1139
+ console.log(`📦 Tamaño de respuesta: ${responseData.length} bytes`);
1140
+
1141
+ if (res.statusCode >= 200 && res.statusCode < 300) {
1142
+ console.log("✅ Publicación exitosa!");
1143
+ try {
1144
+ const response = JSON.parse(responseData);
1145
+ if (response.message) {
1146
+ console.log(`📝 ${response.message}`);
1147
+ }
1148
+ if (response.data) {
1149
+ console.log(`📊 Versión: ${response.data.version_number || "N/A"}`);
1150
+ }
1151
+ if (response.success === false) {
1152
+ console.log(`⚠️ El servidor reportó: success=false`);
1153
+ console.log(`💬 ${JSON.stringify(response, null, 2)}`);
1154
+ }
1155
+ } catch (e) {
1156
+ // No es JSON, mostrar respuesta tal cual
1157
+ if (responseData) {
1158
+ console.log(`📝 Respuesta (texto): ${responseData.substring(0, 500)}`);
1159
+ } else {
1160
+ console.log(`📝 Respuesta vacía`);
1161
+ }
1162
+ }
1163
+ resolve();
1164
+ } else {
1165
+ console.log(`❌ Error en la publicación (${res.statusCode})`);
1166
+ try {
1167
+ const error = JSON.parse(responseData);
1168
+ console.log(`💬 ${error.message || error.error || "Error desconocido"}`);
1169
+ if (error.details) {
1170
+ console.log(`🔍 Detalles:`, JSON.stringify(error.details, null, 2));
1171
+ }
1172
+ } catch (e) {
1173
+ if (responseData) {
1174
+ console.log(`💬 Respuesta del servidor: ${responseData.substring(0, 500)}`);
1175
+ } else {
1176
+ console.log(`💬 Sin respuesta del servidor`);
1177
+ }
1178
+ }
1179
+ reject(new Error(`HTTP ${res.statusCode}: ${responseData}`));
1180
+ }
1181
+ });
1182
+ });
1183
+
1184
+ req.on("error", (error) => {
1185
+ console.log(`❌ Error de conexión: ${error.message}`);
1186
+ console.log(`💡 Código de error: ${error.code || "N/A"}`);
1187
+ console.log(`💡 Stack: ${error.stack || "N/A"}`);
1188
+ console.log(`💡 Verifica que la URL de la API sea correcta: ${apiUrl}`);
1189
+ if (error.code === "ENOTFOUND") {
1190
+ console.log(`💡 No se pudo resolver el hostname: ${url.hostname}`);
1191
+ } else if (error.code === "ECONNREFUSED") {
1192
+ console.log(`💡 Conexión rechazada. ¿El servidor está corriendo?`);
1193
+ } else if (error.code === "ETIMEDOUT") {
1194
+ console.log(`💡 Timeout de conexión. El servidor no respondió a tiempo.`);
1195
+ } else if (error.code === "CERT_HAS_EXPIRED" || error.code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE") {
1196
+ console.log(`💡 Problema con el certificado SSL. Intenta con rejectUnauthorized: false (solo para desarrollo)`);
1197
+ }
1198
+ reject(error);
1199
+ });
1200
+
1201
+ req.setTimeout(60000, () => {
1202
+ console.log(`❌ Timeout: La petición tardó más de 60 segundos sin respuesta`);
1203
+ console.log(`💡 El servidor puede estar sobrecargado o hay un problema de red`);
1204
+ req.destroy();
1205
+ reject(new Error("Timeout"));
1206
+ });
1207
+
1208
+ console.log(`📤 Escribiendo datos en el request...`);
1209
+ req.write(postData);
1210
+ console.log(`✅ Datos escritos, cerrando request...`);
1211
+ req.end();
1212
+
1213
+ console.log(`📨 Petición enviada, esperando respuesta...`);
1214
+
1215
+ // Agregar listener para verificar si el request se está enviando
1216
+ req.on("finish", () => {
1217
+ console.log(`✅ Request finalizado (datos enviados)`);
1218
+ });
1219
+
1220
+ req.on("close", () => {
1221
+ console.log(`🔒 Conexión cerrada`);
1222
+ });
1223
+ }).catch((error) => {
1224
+ console.log(`❌ Error en la promesa: ${error.message}`);
1225
+ throw error;
1226
+ });
1227
+
1228
+ } catch (error) {
1229
+ console.log(`❌ Error al procesar el proyecto: ${error.message}`);
1230
+ if (error.stack) {
1231
+ console.log(error.stack);
1232
+ }
1233
+ process.exit(1);
1234
+ }
745
1235
  }
746
1236
 
747
1237
  function cmdDev(port = 3000) {
@@ -851,6 +1341,23 @@ if (cmd1 === "dev") {
851
1341
  return;
852
1342
  }
853
1343
 
1344
+ if (cmd1 === "publish") {
1345
+ (async () => {
1346
+ try {
1347
+ await cmdPublish(args);
1348
+ process.exit(0);
1349
+ } catch (error) {
1350
+ console.log(`❌ Error: ${error.message}`);
1351
+ if (error.stack) {
1352
+ console.log(error.stack);
1353
+ }
1354
+ process.exit(1);
1355
+ }
1356
+ })();
1357
+ // NO hacer process.exit aquí, dejar que la función async maneje la salida
1358
+ return; // Evitar que el código continúe
1359
+ }
1360
+
854
1361
  console.log("❌ Comando no reconocido.");
855
1362
  printHelp();
856
1363
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mgpanel-cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "MGPanel CLI",
5
5
  "bin": {
6
6
  "mgpanel": "./bin/mgpanel.js"