blackcoffee2 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/CHANGELOG.md +664 -0
- package/LICENSE +201 -0
- package/NOTICE +25 -0
- package/README.md +246 -0
- package/apps.zip +0 -0
- package/bin/adminclient +105 -0
- package/bin/blackcoffee +133 -0
- package/cli/admin-users.js +282 -0
- package/cli/commands/app.js +561 -0
- package/cli/commands/config.js +182 -0
- package/cli/commands/db.js +257 -0
- package/cli/commands/server.js +200 -0
- package/config/applications.json +5 -0
- package/config/database.json +28 -0
- package/config/database.json.example +23 -0
- package/config/server.json +32 -0
- package/controllers/admin/AdminController.js +529 -0
- package/controllers/admin/AdminViewController.js +90 -0
- package/controllers/admin/AuthController.js +293 -0
- package/controllers/admin/DatabaseAdminController.js +218 -0
- package/core/SQLiteAdapter.js +333 -0
- package/core/appLoader.js +385 -0
- package/core/databasePoolManager.js +431 -0
- package/core/hotReload.js +363 -0
- package/data/ADMIN-README.md +145 -0
- package/data/CHANGELOG.md +48 -0
- package/data/GTK3-NODE-PROPOSALS.md +410 -0
- package/data/admin-db.js +150 -0
- package/data/admin-gui.js +452 -0
- package/data/blackcoffee_admin.db-shm +0 -0
- package/data/blackcoffee_admin.db-wal +0 -0
- package/data/migrations/001_create_admin_users.sql +33 -0
- package/docs/APP_HOOKS_HANDLER.md +432 -0
- package/docs/APP_HOOKS_REQUIREMENTS.md +588 -0
- package/docs/ARCHITECTURE.md +435 -0
- package/docs/CREAR_APP_Y_USAR_POOLS.md +1595 -0
- package/docs/EVENTS_APP_MANUAL.md +289 -0
- package/docs/INSITU_BINARY_UPLOAD_PROPOSAL.md +186 -0
- package/docs/INSITU_FIREWALL_EXCEPTION.md +187 -0
- package/docs/ROADMAP.md +242 -0
- package/docs/ROADMAP.md.backup +243 -0
- package/includes/404-hooks.js +423 -0
- package/includes/adminAuth.js +214 -0
- package/includes/adminExtension.js +53 -0
- package/includes/appHooks.js +302 -0
- package/includes/initAdminDb.js +115 -0
- package/includes/routeLoader.js +67 -0
- package/includes/sessions.js +223 -0
- package/issues/001-duplicate-module-loading.md +92 -0
- package/manuales/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
- package/manuales/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
- package/manuales/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
- package/manuales/CACHE_REGEX_COMMANDS.md +136 -0
- package/manuales/CACHE_SYSTEM_MAP.md +206 -0
- package/manuales/CREACION_DE_CONTROLADORES_INSITU.md +383 -0
- package/manuales/QUEUE_CLI_MODULE_MANUAL.md +289 -0
- package/manuales/QUEUE_SYSTEM_MANUAL.md +320 -0
- package/manuales/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
- package/manuales/SESSION_MANAGER_GUIDE.md +529 -0
- package/manuales/SESSION_SECURITY_FLAGS.md +174 -0
- package/manuales/WAF_MODULE_MANUAL.md +229 -0
- package/manuales/after_route_handler_filter_example.md +116 -0
- package/manuales/after_route_handler_usage.md +130 -0
- package/manuales/an/303/241lisis-completo-insitu-framework.md +213 -0
- package/manuales/async_hooks_promises_guide.md +325 -0
- package/manuales/before_route_handler_filter_example.md +97 -0
- package/manuales/before_route_handler_usage.md +122 -0
- package/manuales/hooks_chaining_conditions_guide.md +261 -0
- package/manuales/hooks_filters_documentation.md +493 -0
- package/manuales/hooks_filters_documentation_en.md +493 -0
- package/manuales/hooks_vs_middlewares_comparison.md +87 -0
- package/manuales/manual-mvc-completo.md +934 -0
- package/manuales/modulos_administracion.md +89 -0
- package/manuales/router_execution_points.md +74 -0
- package/manuales/static_file_hooks_usage.md +222 -0
- package/models/AdminUserModel.js +132 -0
- package/package.json +45 -0
- package/programatically/PRoutes.js +89 -0
- package/programatically/initFlow.js +211 -0
- package/public/admin/css/db-pools.css +336 -0
- package/public/admin/css/styles.css +310 -0
- package/public/admin/database.html +312 -0
- package/public/admin/index.html +116 -0
- package/public/admin/js/app.js +470 -0
- package/public/admin/js/db-pools.js +253 -0
- package/public/admin/login.html +278 -0
- package/public/assets/css/styles.css +477 -0
- package/public/assets/js/main.js +89 -0
- package/public/index.html +136 -0
- package/public/templates/404.html +158 -0
- package/routes/admin-views.json +20 -0
- package/routes/admin.json +38 -0
- package/routes/auth.json +32 -0
- package/routes/static.json +18 -0
- package/server.js +299 -0
- package/test-aplicacion.con-logisession/BlackCoffee.js +226 -0
- package/test-aplicacion.con-logisession/SSL_SETUP.md +53 -0
- package/test-aplicacion.con-logisession/certs/ca-certificate.pem +32 -0
- package/test-aplicacion.con-logisession/certs/ca-private-key.pem +52 -0
- package/test-aplicacion.con-logisession/certs/certificate-2048.pem +22 -0
- package/test-aplicacion.con-logisession/certs/certificate.pem +32 -0
- package/test-aplicacion.con-logisession/certs/private-key-2048.pem +28 -0
- package/test-aplicacion.con-logisession/certs/private-key.pem +52 -0
- package/test-aplicacion.con-logisession/config/iaQueueSetup.js +84 -0
- package/test-aplicacion.con-logisession/config/qwen-rules.json +39 -0
- package/test-aplicacion.con-logisession/controllers/analyticsController.js +117 -0
- package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +142 -0
- package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +439 -0
- package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +223 -0
- package/test-aplicacion.con-logisession/controllers/endpointController.js +66 -0
- package/test-aplicacion.con-logisession/controllers/example.js +183 -0
- package/test-aplicacion.con-logisession/controllers/iaQueueController.js +367 -0
- package/test-aplicacion.con-logisession/controllers/queueController.js +206 -0
- package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +197 -0
- package/test-aplicacion.con-logisession/controllers/test.js +0 -0
- package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +78 -0
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +412 -0
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +437 -0
- package/test-aplicacion.con-logisession/hooks/admin-hooks.js +20 -0
- package/test-aplicacion.con-logisession/hooks/general-hooks.js +97 -0
- package/test-aplicacion.con-logisession/hooks/queue-hooks.js +64 -0
- package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +38 -0
- package/test-aplicacion.con-logisession/hooks/security-hooks.js +24 -0
- package/test-aplicacion.con-logisession/insitu-admin-client/README.md +69 -0
- package/test-aplicacion.con-logisession/insitu-admin-client/package.json +23 -0
- package/test-aplicacion.con-logisession/insitu-admin-client.js +257 -0
- package/test-aplicacion.con-logisession/models/ExampleModel.js +88 -0
- package/test-aplicacion.con-logisession/models/QueueJobModel.js +263 -0
- package/test-aplicacion.con-logisession/models/TokenModel.js +207 -0
- package/test-aplicacion.con-logisession/models/auth/AuthModel.js +66 -0
- package/test-aplicacion.con-logisession/models/auth/UserModel.js +189 -0
- package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +213 -0
- package/test-aplicacion.con-logisession/models/tracking/EventModel.js +366 -0
- package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +131 -0
- package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +360 -0
- package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +286 -0
- package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +207 -0
- package/test-aplicacion.con-logisession/package-lock.json +3313 -0
- package/test-aplicacion.con-logisession/package.json +32 -0
- package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +1339 -0
- package/test-aplicacion.con-logisession/public/css/style.css +64 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +18 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +16 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +43 -0
- package/test-aplicacion.con-logisession/public/images/logo.svg +7 -0
- package/test-aplicacion.con-logisession/public/js/main.js +67 -0
- package/test-aplicacion.con-logisession/routes/analytics-routes.json +8 -0
- package/test-aplicacion.con-logisession/routes/auth-routes.json +98 -0
- package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +20 -0
- package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +16 -0
- package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +11 -0
- package/test-aplicacion.con-logisession/routes/endpoints-routes.json +8 -0
- package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +26 -0
- package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +20 -0
- package/test-aplicacion.con-logisession/routes/queue-routes.json +32 -0
- package/test-aplicacion.con-logisession/routes/qwen-routes.json +14 -0
- package/test-aplicacion.con-logisession/routes/static-routes.json +29 -0
- package/test-aplicacion.con-logisession/routes/tracking-routes.json +58 -0
- package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +51 -0
- package/test-aplicacion.con-logisession/utils/dbAdapter.js +88 -0
- package/test-aplicacion.con-logisession/utils/qbWrapper.js +4 -0
- package/test-aplicacion.con-logisession/utils/queueProcessor.js +305 -0
- package/test-aplicacion.con-logisession/utils/qwenRulesService.js +131 -0
- package/test-aplicacion.con-logisession/utils/tokenHelper.js +22 -0
- package/test-aplicacion.con-logisession/views/auth/dashboard.html +443 -0
- package/test-aplicacion.con-logisession/views/auth/forgot-password.html +200 -0
- package/test-aplicacion.con-logisession/views/auth/login.html +213 -0
- package/test-aplicacion.con-logisession/views/auth/register.html +294 -0
- package/test-aplicacion.con-logisession/views/contact/form.html +47 -0
- package/test-aplicacion.con-logisession/views/products/index.html +39 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Cliente CLI para Insitu Framework Administration Server
|
|
2
|
+
|
|
3
|
+
Cliente de línea de comandos para interactuar con el servidor de administración de Insitu Framework. Permite reutilizar comandos y cuenta con autocompletado.
|
|
4
|
+
|
|
5
|
+
## Características
|
|
6
|
+
|
|
7
|
+
- **Autocompletado**: Presiona TAB para autocompletar comandos conocidos
|
|
8
|
+
- **Historial de comandos**: Accede a comandos previamente utilizados
|
|
9
|
+
- **Reutilización de comandos**: Reutiliza comandos por número o por texto
|
|
10
|
+
- **Conservación de contexto**: Mantiene el historial entre sesiones
|
|
11
|
+
|
|
12
|
+
## Instalación
|
|
13
|
+
|
|
14
|
+
El cliente CLI está incluido en el framework Insitu y se puede ejecutar directamente:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node insitu-admin-client.js
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
O con parámetros específicos:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node insitu-admin-client.js --host 127.0.0.1 --port 9999
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Comandos del Cliente
|
|
27
|
+
|
|
28
|
+
Además de los comandos del servidor de administración, el cliente CLI incluye comandos especiales:
|
|
29
|
+
|
|
30
|
+
- `history` - Muestra el historial de comandos
|
|
31
|
+
- `clear` - Limpia la pantalla
|
|
32
|
+
- `re <número>` - Reutiliza el comando número n del historial
|
|
33
|
+
- `re <texto>` - Reutiliza el último comando que contiene el texto
|
|
34
|
+
- `help-cli` - Muestra la ayuda del cliente
|
|
35
|
+
|
|
36
|
+
## Uso
|
|
37
|
+
|
|
38
|
+
1. Inicia el cliente:
|
|
39
|
+
```bash
|
|
40
|
+
node insitu-admin-client.js
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. El cliente se conectará automáticamente al servidor de administración en `localhost:9999`
|
|
44
|
+
|
|
45
|
+
3. Usa los comandos estándar del servidor de administración:
|
|
46
|
+
- `stats` - Ver estadísticas generales
|
|
47
|
+
- `requests` - Ver estadísticas de solicitudes
|
|
48
|
+
- `endpoints` - Ver endpoints más accedidos
|
|
49
|
+
- `routes` - Ver rutas registradas
|
|
50
|
+
- `help` - Ver todos los comandos disponibles
|
|
51
|
+
|
|
52
|
+
4. Usa los comandos especiales del cliente para mejorar la experiencia:
|
|
53
|
+
- `history` para ver comandos anteriores
|
|
54
|
+
- `re 3` para reutilizar el tercer comando del historial
|
|
55
|
+
- `re stats` para reutilizar el último comando que contenía "stats"
|
|
56
|
+
|
|
57
|
+
## Atajos
|
|
58
|
+
|
|
59
|
+
- Presiona `↑` y `↓` para navegar por el historial de comandos
|
|
60
|
+
- Presiona `TAB` para autocompletar comandos
|
|
61
|
+
- Presiona `Ctrl+C` para salir del cliente
|
|
62
|
+
|
|
63
|
+
## Configuración
|
|
64
|
+
|
|
65
|
+
Por defecto, el cliente intenta conectarse a `localhost:9999`. Puedes especificar un host y puerto diferente usando los argumentos `--host` y `--port`.
|
|
66
|
+
|
|
67
|
+
## Archivo de Historial
|
|
68
|
+
|
|
69
|
+
El cliente guarda el historial de comandos en `~/.insitu_admin_history` para conservarlo entre sesiones.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@insitu/admin-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Cliente CLI para interactuar con el servidor de administración de Insitu Framework",
|
|
5
|
+
"main": "insitu-admin-client.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"insitu-admin": "./insitu-admin-client.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node insitu-admin-client.js",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"insitu",
|
|
15
|
+
"admin",
|
|
16
|
+
"cli",
|
|
17
|
+
"client",
|
|
18
|
+
"framework"
|
|
19
|
+
],
|
|
20
|
+
"author": "Insitu Framework Team",
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"dependencies": {}
|
|
23
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cliente CLI para interactuar con el servidor de administración de Insitu Framework
|
|
5
|
+
* Permite reutilizar comandos y tiene autocompletado
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const net = require('net');
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
// Configuración por defecto
|
|
14
|
+
const DEFAULT_HOST = 'localhost';
|
|
15
|
+
const DEFAULT_PORT = 9999;
|
|
16
|
+
|
|
17
|
+
// Historial de comandos
|
|
18
|
+
let commandHistory = [];
|
|
19
|
+
const HISTORY_FILE = path.join(require('os').homedir(), '.insitu_admin_history');
|
|
20
|
+
|
|
21
|
+
// Cargar historial de comandos si existe
|
|
22
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
23
|
+
try {
|
|
24
|
+
const historyData = fs.readFileSync(HISTORY_FILE, 'utf8');
|
|
25
|
+
commandHistory = JSON.parse(historyData);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error al cargar el historial:', error.message);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Comandos conocidos para autocompletado
|
|
32
|
+
const knownCommands = [
|
|
33
|
+
'help',
|
|
34
|
+
'stats',
|
|
35
|
+
'statistics',
|
|
36
|
+
'requests',
|
|
37
|
+
'endpoints',
|
|
38
|
+
'routes',
|
|
39
|
+
'active',
|
|
40
|
+
'version',
|
|
41
|
+
'status',
|
|
42
|
+
'cache',
|
|
43
|
+
'cache-stats',
|
|
44
|
+
'cache-clear',
|
|
45
|
+
'cache-info',
|
|
46
|
+
'sysinfo',
|
|
47
|
+
'system',
|
|
48
|
+
'resources',
|
|
49
|
+
'time',
|
|
50
|
+
'date',
|
|
51
|
+
'quit',
|
|
52
|
+
'exit'
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Función para guardar el historial
|
|
56
|
+
function saveHistory() {
|
|
57
|
+
try {
|
|
58
|
+
fs.writeFileSync(HISTORY_FILE, JSON.stringify(commandHistory, null, 2));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error al guardar el historial:', error.message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Función para autocompletar comandos
|
|
65
|
+
function completer(line) {
|
|
66
|
+
const hits = knownCommands.filter(cmd => cmd.startsWith(line));
|
|
67
|
+
return [hits.length ? hits : knownCommands, line];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Función para crear el cliente
|
|
71
|
+
function createClient(host = DEFAULT_HOST, port = DEFAULT_PORT) {
|
|
72
|
+
const client = new net.Socket();
|
|
73
|
+
|
|
74
|
+
// Conectar al servidor de administración
|
|
75
|
+
client.connect(port, host, () => {
|
|
76
|
+
console.log(`Conectado al servidor de administración en ${host}:${port}`);
|
|
77
|
+
console.log('Escribe "help" para ver los comandos disponibles o "quit" para salir\n');
|
|
78
|
+
|
|
79
|
+
// Configurar readline con autocompletado
|
|
80
|
+
const rl = readline.createInterface({
|
|
81
|
+
input: process.stdin,
|
|
82
|
+
output: process.stdout,
|
|
83
|
+
completer: completer,
|
|
84
|
+
history: commandHistory
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Mostrar historial de comandos recientes
|
|
88
|
+
if (commandHistory.length > 0) {
|
|
89
|
+
console.log(`Comandos recientes: ${commandHistory.slice(-5).join(', ')}\n`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Función para manejar la entrada del usuario
|
|
93
|
+
function handleInput() {
|
|
94
|
+
rl.question('> ', (input) => {
|
|
95
|
+
const command = input.trim();
|
|
96
|
+
|
|
97
|
+
if (command.toLowerCase() === 'quit' || command.toLowerCase() === 'exit') {
|
|
98
|
+
console.log('Desconectando del servidor...');
|
|
99
|
+
client.end();
|
|
100
|
+
rl.close();
|
|
101
|
+
saveHistory();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (command.toLowerCase() === 'history') {
|
|
106
|
+
console.log('Historial de comandos:');
|
|
107
|
+
commandHistory.forEach((cmd, index) => {
|
|
108
|
+
console.log(`${index + 1}. ${cmd}`);
|
|
109
|
+
});
|
|
110
|
+
handleInput();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (command.toLowerCase() === 'clear') {
|
|
115
|
+
console.clear();
|
|
116
|
+
handleInput();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (command.toLowerCase() === 'help-cli') {
|
|
121
|
+
console.log('\n--- Comandos del Cliente CLI ---');
|
|
122
|
+
console.log('help-cli - Muestra esta ayuda');
|
|
123
|
+
console.log('history - Muestra el historial de comandos');
|
|
124
|
+
console.log('clear - Limpia la pantalla');
|
|
125
|
+
console.log('re <número> - Reutiliza el comando número n del historial');
|
|
126
|
+
console.log('re <texto> - Reutiliza el último comando que contiene el texto');
|
|
127
|
+
console.log('-----------------------------\n');
|
|
128
|
+
handleInput();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Comando para reutilizar comandos del historial
|
|
133
|
+
if (command.toLowerCase().startsWith('re ')) {
|
|
134
|
+
const arg = command.substring(3).trim();
|
|
135
|
+
|
|
136
|
+
if (/^\d+$/.test(arg)) {
|
|
137
|
+
// Reutilizar por número
|
|
138
|
+
const index = parseInt(arg) - 1;
|
|
139
|
+
if (index >= 0 && index < commandHistory.length) {
|
|
140
|
+
const reusedCmd = commandHistory[index];
|
|
141
|
+
console.log(`Reutilizando comando: ${reusedCmd}`);
|
|
142
|
+
client.write(reusedCmd + '\n');
|
|
143
|
+
if (!commandHistory.includes(reusedCmd)) {
|
|
144
|
+
commandHistory.push(reusedCmd);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
console.log(`Número de comando inválido: ${arg}`);
|
|
148
|
+
handleInput();
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Reutilizar por texto
|
|
152
|
+
const matchingCmd = commandHistory.reverse().find(cmd => cmd.includes(arg));
|
|
153
|
+
commandHistory.reverse(); // Restaurar orden original
|
|
154
|
+
|
|
155
|
+
if (matchingCmd) {
|
|
156
|
+
console.log(`Reutilizando comando: ${matchingCmd}`);
|
|
157
|
+
client.write(matchingCmd + '\n');
|
|
158
|
+
} else {
|
|
159
|
+
console.log(`No se encontró comando que contenga: ${arg}`);
|
|
160
|
+
handleInput();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} else if (command) {
|
|
164
|
+
// Enviar comando al servidor
|
|
165
|
+
client.write(command + '\n');
|
|
166
|
+
|
|
167
|
+
// Agregar al historial si no está repetido
|
|
168
|
+
if (!commandHistory.includes(command)) {
|
|
169
|
+
commandHistory.push(command);
|
|
170
|
+
// Mantener solo los últimos 100 comandos
|
|
171
|
+
if (commandHistory.length > 100) {
|
|
172
|
+
commandHistory = commandHistory.slice(-100);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
handleInput();
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Iniciar la interacción
|
|
182
|
+
handleInput();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Manejar datos recibidos del servidor
|
|
186
|
+
client.on('data', (data) => {
|
|
187
|
+
const output = data.toString();
|
|
188
|
+
process.stdout.write(output);
|
|
189
|
+
|
|
190
|
+
// Si la salida termina con el prompt, significa que el comando terminó
|
|
191
|
+
if (output.endsWith('> ')) {
|
|
192
|
+
// No hacer nada, dejar que readline continúe
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Manejar errores
|
|
197
|
+
client.on('error', (error) => {
|
|
198
|
+
console.error(`Error de conexión: ${error.message}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Manejar cierre de conexión
|
|
203
|
+
client.on('close', () => {
|
|
204
|
+
console.log('\nConexión cerrada');
|
|
205
|
+
process.exit(0);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Mostrar ayuda
|
|
210
|
+
function showHelp() {
|
|
211
|
+
console.log(`
|
|
212
|
+
Cliente CLI para Insitu Framework Administration Server
|
|
213
|
+
|
|
214
|
+
Uso: node insitu-admin-client.js [opciones]
|
|
215
|
+
|
|
216
|
+
Opciones:
|
|
217
|
+
-h, --host HOST Host del servidor (por defecto: ${DEFAULT_HOST})
|
|
218
|
+
-p, --port PORT Puerto del servidor (por defecto: ${DEFAULT_PORT})
|
|
219
|
+
--help Mostrar esta ayuda
|
|
220
|
+
|
|
221
|
+
Comandos especiales del cliente:
|
|
222
|
+
history Mostrar historial de comandos
|
|
223
|
+
clear Limpiar la pantalla
|
|
224
|
+
re <número> Reutilizar comando por número
|
|
225
|
+
re <texto> Reutilizar último comando que contiene texto
|
|
226
|
+
help-cli Mostrar ayuda del cliente
|
|
227
|
+
|
|
228
|
+
Ejemplos:
|
|
229
|
+
node insitu-admin-client.js
|
|
230
|
+
node insitu-admin-client.js --host 127.0.0.1 --port 9999
|
|
231
|
+
`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Parsear argumentos
|
|
235
|
+
const args = process.argv.slice(2);
|
|
236
|
+
let host = DEFAULT_HOST;
|
|
237
|
+
let port = DEFAULT_PORT;
|
|
238
|
+
|
|
239
|
+
for (let i = 0; i < args.length; i++) {
|
|
240
|
+
if (args[i] === '--help' || args[i] === '-h') {
|
|
241
|
+
showHelp();
|
|
242
|
+
process.exit(0);
|
|
243
|
+
} else if (args[i] === '--host' || args[i] === '-h') {
|
|
244
|
+
host = args[++i];
|
|
245
|
+
} else if (args[i] === '--port' || args[i] === '-p') {
|
|
246
|
+
port = parseInt(args[++i]);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Validar puerto
|
|
251
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
252
|
+
console.error(`Puerto inválido: ${port}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Iniciar el cliente
|
|
257
|
+
createClient(host, port);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const { ModelBase } = require('insitu-js');
|
|
2
|
+
const { getSharedAdapter } = require('../utils/dbAdapter');
|
|
3
|
+
|
|
4
|
+
class ProductModel extends ModelBase {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
// Obtener el adaptador centralizado
|
|
7
|
+
const adapter = getSharedAdapter();
|
|
8
|
+
|
|
9
|
+
super({
|
|
10
|
+
...options,
|
|
11
|
+
tableName: options.tableName || 'products',
|
|
12
|
+
adapter: adapter
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Método para obtener todos los productos
|
|
17
|
+
async getAllProducts() {
|
|
18
|
+
try {
|
|
19
|
+
// En lugar de hacer una consulta real a la base de datos,
|
|
20
|
+
// devolvemos datos de ejemplo para demostrar el funcionamiento
|
|
21
|
+
return [
|
|
22
|
+
{ id: 1, name: 'Laptop', description: 'Computadora portátil de alta gama', price: 1200, category: 'Electrónica' },
|
|
23
|
+
{ id: 2, name: 'Teléfono', description: 'Smartphone con cámara de alta resolución', price: 800, category: 'Electrónica' },
|
|
24
|
+
{ id: 3, name: 'Teclado', description: 'Teclado mecánico RGB', price: 100, category: 'Accesorios' },
|
|
25
|
+
{ id: 4, name: 'Mouse', description: 'Mouse inalámbrico ergonómico', price: 50, category: 'Accesorios' },
|
|
26
|
+
{ id: 5, name: 'Monitor', description: 'Monitor 4K de 27 pulgadas', price: 400, category: 'Electrónica' }
|
|
27
|
+
];
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(`Error al obtener productos: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Método para obtener un producto por ID
|
|
34
|
+
async getProductById(id) {
|
|
35
|
+
try {
|
|
36
|
+
const products = await this.getAllProducts();
|
|
37
|
+
return products.find(product => product.id == id) || null;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw new Error(`Error al obtener producto por ID: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Método para crear un nuevo producto
|
|
44
|
+
async createProduct(productData) {
|
|
45
|
+
try {
|
|
46
|
+
// Validar los datos del producto
|
|
47
|
+
if (!productData.name || !productData.description || !productData.price || !productData.category) {
|
|
48
|
+
throw new Error('Todos los campos son obligatorios: name, description, price, category');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// En una implementación real, aquí haríamos la inserción en la base de datos
|
|
52
|
+
// Por ahora, devolvemos un objeto simulando la creación
|
|
53
|
+
return {
|
|
54
|
+
id: Math.floor(Math.random() * 1000), // ID simulado
|
|
55
|
+
...productData
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Error al crear producto: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Método para actualizar un producto
|
|
63
|
+
async updateProduct(id, productData) {
|
|
64
|
+
try {
|
|
65
|
+
// En una implementación real, aquí haríamos la actualización en la base de datos
|
|
66
|
+
// Por ahora, devolvemos un objeto simulando la actualización
|
|
67
|
+
return {
|
|
68
|
+
id: parseInt(id),
|
|
69
|
+
...productData
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(`Error al actualizar producto: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Método para eliminar un producto
|
|
77
|
+
async deleteProduct(id) {
|
|
78
|
+
try {
|
|
79
|
+
// En una implementación real, aquí haríamos la eliminación en la base de datos
|
|
80
|
+
// Por ahora, simulamos la eliminación
|
|
81
|
+
return { success: true, message: `Producto con ID ${id} eliminado` };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(`Error al eliminar producto: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = ProductModel;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
const { hooks } = require('insitu-js');
|
|
2
|
+
|
|
3
|
+
class QueueJobModel {
|
|
4
|
+
constructor() {
|
|
5
|
+
// Cola de trabajos en memoria
|
|
6
|
+
this.jobs = new Map();
|
|
7
|
+
// Colas específicas para diferentes tipos de trabajos
|
|
8
|
+
this.queues = new Map();
|
|
9
|
+
// Trabajos en proceso
|
|
10
|
+
this.processingJobs = new Map();
|
|
11
|
+
// Trabajos completados
|
|
12
|
+
this.completedJobs = new Map();
|
|
13
|
+
// Trabajos fallidos
|
|
14
|
+
this.failedJobs = new Map();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Crear un nuevo trabajo en la cola
|
|
18
|
+
async createJob(jobData) {
|
|
19
|
+
// Aplicar filtro para manipular los datos del trabajo antes de crearlo
|
|
20
|
+
const filteredJobData = await hooks.applyFiltersAsync('filter_queue_job_data', jobData);
|
|
21
|
+
|
|
22
|
+
// Disparar hook antes de crear el trabajo
|
|
23
|
+
await hooks.doActionAsync('queue_job_created_before', filteredJobData);
|
|
24
|
+
|
|
25
|
+
const jobId = this.generateId();
|
|
26
|
+
const job = {
|
|
27
|
+
id: jobId,
|
|
28
|
+
queue: filteredJobData.queue || 'default',
|
|
29
|
+
payload: filteredJobData.payload,
|
|
30
|
+
status: 'pending',
|
|
31
|
+
attempts: 0,
|
|
32
|
+
max_attempts: filteredJobData.max_attempts || 3,
|
|
33
|
+
created_at: new Date().toISOString(),
|
|
34
|
+
updated_at: new Date().toISOString(),
|
|
35
|
+
scheduled_for: filteredJobData.scheduled_for || new Date().toISOString(),
|
|
36
|
+
priority: filteredJobData.priority || 0,
|
|
37
|
+
result: null,
|
|
38
|
+
error: null
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Añadir el trabajo a la cola correspondiente
|
|
42
|
+
if (!this.queues.has(job.queue)) {
|
|
43
|
+
this.queues.set(job.queue, []);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Disparar hook antes de ordenar la cola
|
|
47
|
+
await hooks.doActionAsync('queue_before_sort', job.queue, this.queues.get(job.queue));
|
|
48
|
+
|
|
49
|
+
// Añadir el trabajo a la cola, ordenado por prioridad
|
|
50
|
+
const queue = this.queues.get(job.queue);
|
|
51
|
+
queue.push(job);
|
|
52
|
+
|
|
53
|
+
// Ordenar por prioridad descendente y fecha de creación ascendente
|
|
54
|
+
queue.sort((a, b) => {
|
|
55
|
+
if (b.priority !== a.priority) {
|
|
56
|
+
return b.priority - a.priority;
|
|
57
|
+
}
|
|
58
|
+
return new Date(a.created_at) - new Date(b.created_at);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Disparar hook después de ordenar la cola
|
|
62
|
+
await hooks.doActionAsync('queue_after_sort', job.queue, queue);
|
|
63
|
+
|
|
64
|
+
// Registrar el trabajo en el mapa general
|
|
65
|
+
this.jobs.set(jobId, job);
|
|
66
|
+
|
|
67
|
+
// Disparar hook después de crear el trabajo
|
|
68
|
+
await hooks.doActionAsync('queue_job_created', job);
|
|
69
|
+
|
|
70
|
+
return job;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Obtener un trabajo pendiente de una cola específica
|
|
74
|
+
async getNextPendingJob(queue = 'default') {
|
|
75
|
+
// Disparar hook antes de obtener un trabajo pendiente
|
|
76
|
+
await hooks.doActionAsync('queue_get_pending_job_before', queue);
|
|
77
|
+
|
|
78
|
+
if (!this.queues.has(queue)) {
|
|
79
|
+
// Disparar hook si la cola no existe
|
|
80
|
+
await hooks.doActionAsync('queue_not_found', queue);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const queueArray = this.queues.get(queue);
|
|
85
|
+
const now = new Date();
|
|
86
|
+
|
|
87
|
+
// Buscar el siguiente trabajo pendiente programado para ahora o antes
|
|
88
|
+
for (let i = 0; i < queueArray.length; i++) {
|
|
89
|
+
const job = queueArray[i];
|
|
90
|
+
if (job.status === 'pending' && new Date(job.scheduled_for) <= now) {
|
|
91
|
+
// Disparar hook cuando se encuentra un trabajo pendiente
|
|
92
|
+
await hooks.doActionAsync('queue_pending_job_found', job, queue);
|
|
93
|
+
return job;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Disparar hook si no se encuentra trabajo pendiente
|
|
98
|
+
await hooks.doActionAsync('queue_no_pending_job', queue);
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Marcar un trabajo como en proceso
|
|
104
|
+
async markJobAsProcessing(jobId) {
|
|
105
|
+
const job = this.jobs.get(jobId);
|
|
106
|
+
if (job) {
|
|
107
|
+
// Disparar hook antes de marcar como en proceso
|
|
108
|
+
await hooks.doActionAsync('queue_job_processing_before', job);
|
|
109
|
+
|
|
110
|
+
job.status = 'processing';
|
|
111
|
+
job.updated_at = new Date().toISOString();
|
|
112
|
+
this.processingJobs.set(jobId, job);
|
|
113
|
+
|
|
114
|
+
// Remover de la cola pendiente
|
|
115
|
+
const queue = this.queues.get(job.queue);
|
|
116
|
+
if (queue) {
|
|
117
|
+
const index = queue.findIndex(j => j.id === jobId);
|
|
118
|
+
if (index !== -1) {
|
|
119
|
+
queue.splice(index, 1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Disparar hook después de marcar como en proceso
|
|
124
|
+
await hooks.doActionAsync('queue_job_processing', job);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Marcar un trabajo como completado
|
|
129
|
+
async markJobAsCompleted(jobId, result = null) {
|
|
130
|
+
const job = this.jobs.get(jobId);
|
|
131
|
+
if (job) {
|
|
132
|
+
// Aplicar filtro para manipular el resultado antes de guardarlo
|
|
133
|
+
const filteredResult = await hooks.applyFiltersAsync('filter_queue_job_result', result, job);
|
|
134
|
+
|
|
135
|
+
// Disparar hook antes de marcar como completado
|
|
136
|
+
await hooks.doActionAsync('queue_job_completed_before', job, filteredResult);
|
|
137
|
+
|
|
138
|
+
job.status = 'completed';
|
|
139
|
+
job.result = filteredResult;
|
|
140
|
+
job.updated_at = new Date().toISOString();
|
|
141
|
+
|
|
142
|
+
// Mover de processing a completed
|
|
143
|
+
this.processingJobs.delete(jobId);
|
|
144
|
+
this.completedJobs.set(jobId, job);
|
|
145
|
+
|
|
146
|
+
// Disparar hook después de marcar como completado
|
|
147
|
+
await hooks.doActionAsync('queue_job_completed', job);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Marcar un trabajo como fallido
|
|
152
|
+
async markJobAsFailed(jobId, error = null) {
|
|
153
|
+
const job = this.jobs.get(jobId);
|
|
154
|
+
if (job) {
|
|
155
|
+
// Aplicar filtro para manipular el error antes de guardarlo
|
|
156
|
+
const filteredError = await hooks.applyFiltersAsync('filter_queue_job_error', error, job);
|
|
157
|
+
|
|
158
|
+
// Disparar hook antes de marcar como fallido
|
|
159
|
+
await hooks.doActionAsync('queue_job_failed_before', job, filteredError);
|
|
160
|
+
|
|
161
|
+
job.status = 'failed';
|
|
162
|
+
job.error = filteredError;
|
|
163
|
+
job.attempts += 1;
|
|
164
|
+
job.updated_at = new Date().toISOString();
|
|
165
|
+
|
|
166
|
+
// Mover de processing a failed
|
|
167
|
+
this.processingJobs.delete(jobId);
|
|
168
|
+
this.failedJobs.set(jobId, job);
|
|
169
|
+
|
|
170
|
+
// Disparar hook después de marcar como fallido
|
|
171
|
+
await hooks.doActionAsync('queue_job_failed', job, filteredError);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Verificar si un trabajo debe reintetntarse
|
|
176
|
+
async shouldRetryJob(jobId, error = null) {
|
|
177
|
+
const job = this.jobs.get(jobId);
|
|
178
|
+
if (job) {
|
|
179
|
+
// Disparar hook para tomar decisiones personalizadas sobre si reintentar un trabajo
|
|
180
|
+
const customRetryDecision = await hooks.applyFiltersAsync('queue_job_should_retry', null, job, error);
|
|
181
|
+
|
|
182
|
+
if (customRetryDecision !== null) {
|
|
183
|
+
return customRetryDecision;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// No reintentar si es un error de seguridad
|
|
187
|
+
if (error && typeof error === 'string' && error.includes('SECURITY_ERROR')) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return job.attempts < job.max_attempts;
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Obtener el estado de un trabajo
|
|
196
|
+
async getJobStatus(jobId) {
|
|
197
|
+
// Disparar hook antes de obtener el estado del trabajo
|
|
198
|
+
await hooks.doActionAsync('queue_get_job_status_before', jobId);
|
|
199
|
+
|
|
200
|
+
let job = null;
|
|
201
|
+
|
|
202
|
+
if (this.jobs.has(jobId)) {
|
|
203
|
+
job = this.jobs.get(jobId);
|
|
204
|
+
} else if (this.processingJobs.has(jobId)) {
|
|
205
|
+
job = this.processingJobs.get(jobId);
|
|
206
|
+
} else if (this.completedJobs.has(jobId)) {
|
|
207
|
+
job = this.completedJobs.get(jobId);
|
|
208
|
+
} else if (this.failedJobs.has(jobId)) {
|
|
209
|
+
job = this.failedJobs.get(jobId);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Disparar hook después de obtener el estado del trabajo
|
|
213
|
+
await hooks.doActionAsync('queue_get_job_status_after', job, jobId);
|
|
214
|
+
|
|
215
|
+
return job;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Generar un ID único para el trabajo
|
|
219
|
+
generateId() {
|
|
220
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Obtener trabajos por estado
|
|
224
|
+
getJobsByStatus(status) {
|
|
225
|
+
// Disparar hook antes de obtener trabajos por estado
|
|
226
|
+
hooks.doAction('queue_get_jobs_by_status_before', status);
|
|
227
|
+
|
|
228
|
+
let jobs = [];
|
|
229
|
+
|
|
230
|
+
switch(status) {
|
|
231
|
+
case 'pending':
|
|
232
|
+
// Obtener todos los trabajos pendientes de todas las colas
|
|
233
|
+
const pendingJobs = [];
|
|
234
|
+
for (const queue of this.queues.values()) {
|
|
235
|
+
pendingJobs.push(...queue.filter(job => job.status === 'pending'));
|
|
236
|
+
}
|
|
237
|
+
jobs = pendingJobs;
|
|
238
|
+
break;
|
|
239
|
+
case 'processing':
|
|
240
|
+
jobs = Array.from(this.processingJobs.values());
|
|
241
|
+
break;
|
|
242
|
+
case 'completed':
|
|
243
|
+
jobs = Array.from(this.completedJobs.values());
|
|
244
|
+
break;
|
|
245
|
+
case 'failed':
|
|
246
|
+
jobs = Array.from(this.failedJobs.values());
|
|
247
|
+
break;
|
|
248
|
+
default:
|
|
249
|
+
jobs = [];
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Disparar hook después de obtener trabajos por estado
|
|
254
|
+
hooks.doAction('queue_get_jobs_by_status_after', jobs, status);
|
|
255
|
+
|
|
256
|
+
return jobs;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Instancia global del modelo de cola
|
|
261
|
+
const globalQueueJobModel = new QueueJobModel();
|
|
262
|
+
|
|
263
|
+
module.exports = globalQueueJobModel;
|