entexto-cli 1.4.9 → 2.0.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/bin/entexto.js +39 -0
- package/docs/fullappdeploy.md +657 -0
- package/lib/commands/api-platform.js +258 -0
- package/lib/commands/create.js +415 -0
- package/lib/commands/deploy.js +2 -2
- package/lib/commands/domain.js +222 -0
- package/lib/commands/live.js +105 -0
- package/lib/commands/publish.js +51 -0
- package/lib/utils/api.js +81 -1
- package/package.json +10 -4
package/bin/entexto.js
CHANGED
|
@@ -85,6 +85,45 @@ program
|
|
|
85
85
|
require('../lib/commands/tunnel')({ target: target || '3000' });
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
// ─── entexto create ──────────────────────────────────────────
|
|
89
|
+
program
|
|
90
|
+
.command('create')
|
|
91
|
+
.description('Crear un nuevo proyecto (interactivo o con opciones)')
|
|
92
|
+
.option('-n, --name <name>', 'Nombre del proyecto')
|
|
93
|
+
.option('-t, --template <type>', 'Plantilla: blank | basic | react | fullstack | local')
|
|
94
|
+
.option('-d, --dir <path>', 'Carpeta a subir (con template=local)', '.')
|
|
95
|
+
.action(require('../lib/commands/create'));
|
|
96
|
+
|
|
97
|
+
// ─── entexto domain ──────────────────────────────────────────
|
|
98
|
+
program
|
|
99
|
+
.command('domain [action] [value]')
|
|
100
|
+
.description('Gestionar dominios: list | add | subdomain | verify')
|
|
101
|
+
.option('-p, --project <id>', 'ID del proyecto a vincular')
|
|
102
|
+
.action(require('../lib/commands/domain'));
|
|
103
|
+
|
|
104
|
+
// ─── entexto publish ─────────────────────────────────────────
|
|
105
|
+
program
|
|
106
|
+
.command('publish')
|
|
107
|
+
.description('Publicar un proyecto rápidamente')
|
|
108
|
+
.option('-i, --id <uuid>', 'UUID del proyecto a publicar')
|
|
109
|
+
.action((options) => {
|
|
110
|
+
require('../lib/commands/publish')({ uuid: options.id });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ─── entexto api ──────────────────────────────────────────────
|
|
114
|
+
program
|
|
115
|
+
.command('api [action] [slug]')
|
|
116
|
+
.description('API Platform: list | create | info | collection | insert | query')
|
|
117
|
+
.option('-n, --name <name>', 'Nombre')
|
|
118
|
+
.option('-D, --description <desc>', 'Descripción')
|
|
119
|
+
.action(require('../lib/commands/api-platform'));
|
|
120
|
+
|
|
121
|
+
// ─── entexto live ─────────────────────────────────────────────
|
|
122
|
+
program
|
|
123
|
+
.command('live')
|
|
124
|
+
.description('Guía del Live SDK (WebRTC, videollamadas, tiempo real)')
|
|
125
|
+
.action(require('../lib/commands/live'));
|
|
126
|
+
|
|
88
127
|
program.parse(process.argv);
|
|
89
128
|
|
|
90
129
|
if (!process.argv.slice(2).length) {
|
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
# fullappdeploy.md — Guía Completa para Desplegar Apps con EntExto CLI
|
|
2
|
+
|
|
3
|
+
> **Versión**: 2.0.0
|
|
4
|
+
> **Para**: IAs y desarrolladores que necesiten crear, configurar y desplegar una aplicación completa desde la terminal.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Instalación
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g entexto-cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Verifica la instalación:
|
|
15
|
+
```bash
|
|
16
|
+
entexto --version
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Autenticación
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
entexto login
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Ingresa tu email y contraseña de entexto.com. El token se guarda en `~/.entexto/config.json`.
|
|
28
|
+
|
|
29
|
+
**Verificar sesión:**
|
|
30
|
+
```bash
|
|
31
|
+
entexto whoami
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Cerrar sesión:**
|
|
35
|
+
```bash
|
|
36
|
+
entexto logout
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 3. Crear un Proyecto
|
|
42
|
+
|
|
43
|
+
### Modo interactivo
|
|
44
|
+
```bash
|
|
45
|
+
entexto create
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
El CLI te guía paso a paso:
|
|
49
|
+
1. Nombre del proyecto
|
|
50
|
+
2. Descripción
|
|
51
|
+
3. Plantilla (blank, basic, react, fullstack, local)
|
|
52
|
+
4. ¿Publicar ahora?
|
|
53
|
+
5. ¿Vincular dominio/subdominio?
|
|
54
|
+
|
|
55
|
+
### Con opciones directas
|
|
56
|
+
```bash
|
|
57
|
+
entexto create --name "Mi App" --template react
|
|
58
|
+
entexto create --name "Backend" --template fullstack
|
|
59
|
+
entexto create --name "Desde carpeta" --template local --dir ./mi-proyecto
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Plantillas disponibles
|
|
63
|
+
|
|
64
|
+
| Plantilla | Descripción |
|
|
65
|
+
|-------------|-------------|
|
|
66
|
+
| `blank` | Proyecto vacío con index.html mínimo |
|
|
67
|
+
| `basic` | HTML + CSS + JS con estructura base |
|
|
68
|
+
| `react` | React con CDN (Babel + ReactDOM) listo para JSX |
|
|
69
|
+
| `fullstack` | React + VFS (sistema de archivos virtual) + Live SDK preconfigurado |
|
|
70
|
+
| `local` | Sube una carpeta existente de tu disco como proyecto |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 4. Desplegar Archivos
|
|
75
|
+
|
|
76
|
+
### Deploy básico
|
|
77
|
+
```bash
|
|
78
|
+
entexto deploy --id <uuid> --dir ./mi-proyecto
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Deploy con publicación automática
|
|
82
|
+
```bash
|
|
83
|
+
entexto deploy --id <uuid> --dir ./mi-proyecto --publish
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Deploy con watch (live reload)
|
|
87
|
+
```bash
|
|
88
|
+
entexto deploy --id <uuid> --dir ./mi-proyecto --watch
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Deploy completo (sin delta)
|
|
92
|
+
```bash
|
|
93
|
+
entexto deploy --id <uuid> --dir ./mi-proyecto --full
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> **Nota**: El deploy usa delta por defecto — solo sube archivos que cambiaron (basado en SHA256).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 5. Publicar un Proyecto
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
entexto publish --id <uuid>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
O selecciona de la lista interactiva:
|
|
107
|
+
```bash
|
|
108
|
+
entexto publish
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Una vez publicado, el proyecto es accesible en: `https://entexto.com/p/<uuid>`
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 6. Gestión de Dominios
|
|
116
|
+
|
|
117
|
+
### Listar dominios configurados
|
|
118
|
+
```bash
|
|
119
|
+
entexto domain list
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Agregar un dominio propio
|
|
123
|
+
```bash
|
|
124
|
+
entexto domain add midominio.com --project <id>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
El CLI mostrará las instrucciones DNS:
|
|
128
|
+
- **Opción A**: Registro A apuntando al IP del servidor
|
|
129
|
+
- **Opción B**: CNAME apuntando a `entexto.com`
|
|
130
|
+
- **Opción C**: CNAME apuntando al tunnel de Cloudflare
|
|
131
|
+
|
|
132
|
+
### Crear un subdominio de un dominio verificado
|
|
133
|
+
```bash
|
|
134
|
+
entexto domain subdomain --project <id>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Selecciona el dominio padre y escribe el subdominio deseado (ej: `app` → `app.midominio.com`).
|
|
138
|
+
|
|
139
|
+
### Verificar un dominio
|
|
140
|
+
```bash
|
|
141
|
+
entexto domain verify midominio.com
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Métodos de verificación soportados:
|
|
145
|
+
1. Registro A directo
|
|
146
|
+
2. CNAME a cfargotunnel.com
|
|
147
|
+
3. CNAME a entexto.com
|
|
148
|
+
4. NS delegation (ns1/ns2.entexto.com)
|
|
149
|
+
5. HTTP ping fallback
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 7. API Platform (BaaS)
|
|
154
|
+
|
|
155
|
+
EntExto incluye un Backend-as-a-Service completo. Gestiona APIs REST desde la terminal.
|
|
156
|
+
|
|
157
|
+
### Crear una API
|
|
158
|
+
```bash
|
|
159
|
+
entexto api create
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Responde: nombre y descripción. Recibirás:
|
|
163
|
+
- **Slug** para las URLs
|
|
164
|
+
- **API Key** para autenticarte
|
|
165
|
+
- **Base URL**: `https://api.entexto.com/v1/api/<slug>`
|
|
166
|
+
|
|
167
|
+
### Listar tus APIs
|
|
168
|
+
```bash
|
|
169
|
+
entexto api list
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Ver detalle de una API
|
|
173
|
+
```bash
|
|
174
|
+
entexto api info <slug>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Crear una colección
|
|
178
|
+
```bash
|
|
179
|
+
entexto api collection <slug>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Insertar un documento
|
|
183
|
+
```bash
|
|
184
|
+
entexto api insert <slug>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Se abre un editor para escribir el JSON del documento.
|
|
188
|
+
|
|
189
|
+
### Consultar documentos
|
|
190
|
+
```bash
|
|
191
|
+
entexto api query <slug>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Usar la API desde código
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// Autenticación con API Key
|
|
198
|
+
const resp = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
headers: {
|
|
201
|
+
'Content-Type': 'application/json',
|
|
202
|
+
'X-API-Key': 'tu-api-key'
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify({
|
|
205
|
+
data: { nombre: 'Juan', email: 'juan@test.com' }
|
|
206
|
+
})
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// O con JWT
|
|
210
|
+
const login = await fetch('https://api.entexto.com/v1/api/auth/login', {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
headers: { 'Content-Type': 'application/json' },
|
|
213
|
+
body: JSON.stringify({ email: 'tu@email.com', password: 'tu-password' })
|
|
214
|
+
});
|
|
215
|
+
const { token } = await login.json();
|
|
216
|
+
|
|
217
|
+
const docs = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
|
|
218
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 8. Live SDK (Tiempo Real + WebRTC)
|
|
225
|
+
|
|
226
|
+
### ¿Qué es?
|
|
227
|
+
El Live SDK permite agregar comunicación en tiempo real a cualquier proyecto: chat, videollamadas, audio, pantalla compartida, y eventos.
|
|
228
|
+
|
|
229
|
+
### Ver la guía completa
|
|
230
|
+
```bash
|
|
231
|
+
entexto live
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Uso rápido en un proyecto publicado
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<!-- El SDK se inyecta automáticamente en proyectos publicados -->
|
|
238
|
+
<script>
|
|
239
|
+
// Crear/unirse a una sala
|
|
240
|
+
const sala = window.__entexto.live.sala('mi-sala', {
|
|
241
|
+
usuario: 'Pedro'
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Escuchar eventos
|
|
245
|
+
sala.escuchar('mensaje', (data, remitente) => {
|
|
246
|
+
console.log(remitente, 'dice:', data);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Enviar a todos
|
|
250
|
+
sala.emitir('mensaje', { texto: 'Hola a todos' });
|
|
251
|
+
|
|
252
|
+
// Presencia
|
|
253
|
+
sala.onEntra((usuario, socketId) => {
|
|
254
|
+
console.log(usuario, 'entró');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
sala.onSale((usuario, socketId) => {
|
|
258
|
+
console.log(usuario, 'salió');
|
|
259
|
+
});
|
|
260
|
+
</script>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Métodos disponibles
|
|
264
|
+
|
|
265
|
+
| Método | Descripción |
|
|
266
|
+
|--------|-------------|
|
|
267
|
+
| `sala.escuchar(evento, callback)` | Escuchar un evento de la sala |
|
|
268
|
+
| `sala.emitir(evento, data)` | Enviar a todos en la sala |
|
|
269
|
+
| `sala.emitirA(socketId, evento, data)` | Enviar a un usuario específico |
|
|
270
|
+
| `sala.onRelay(callback)` | Recibir señalización WebRTC |
|
|
271
|
+
| `sala.onEntra(callback)` | Evento: usuario entró |
|
|
272
|
+
| `sala.onSale(callback)` | Evento: usuario salió |
|
|
273
|
+
| `sala.salir()` | Salir de la sala |
|
|
274
|
+
|
|
275
|
+
### Videollamadas WebRTC
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
// 1. Obtener media local
|
|
279
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
280
|
+
video: true,
|
|
281
|
+
audio: true
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// 2. Obtener config TURN (automático en EntExto)
|
|
285
|
+
const resp = await fetch('/api/turn-config');
|
|
286
|
+
const { iceServers } = await resp.json();
|
|
287
|
+
|
|
288
|
+
// 3. Crear conexión WebRTC
|
|
289
|
+
const pc = new RTCPeerConnection({ iceServers });
|
|
290
|
+
|
|
291
|
+
// 4. Agregar tracks
|
|
292
|
+
stream.getTracks().forEach(track => pc.addTrack(track, stream));
|
|
293
|
+
|
|
294
|
+
// 5. Negociar via Live SDK
|
|
295
|
+
pc.onicecandidate = (e) => {
|
|
296
|
+
if (e.candidate) sala.emitir('relay', { target: peerId, candidate: e.candidate });
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
pc.createOffer().then(offer => {
|
|
300
|
+
pc.setLocalDescription(offer);
|
|
301
|
+
sala.emitir('relay', { target: peerId, sdp: offer });
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
sala.onRelay((data, fromId) => {
|
|
305
|
+
if (data.sdp) {
|
|
306
|
+
pc.setRemoteDescription(data.sdp);
|
|
307
|
+
if (data.sdp.type === 'offer') {
|
|
308
|
+
pc.createAnswer().then(answer => {
|
|
309
|
+
pc.setLocalDescription(answer);
|
|
310
|
+
sala.emitir('relay', { target: fromId, sdp: answer });
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (data.candidate) pc.addIceCandidate(data.candidate);
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### ICE Servers
|
|
319
|
+
- **STUN**: `stun:entexto.com:3478`
|
|
320
|
+
- **TURN**: Credenciales automáticas desde `/api/turn-config` (HMAC-SHA1, TTL 24h)
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 9. Flujo Completo Automatizado (para IAs)
|
|
325
|
+
|
|
326
|
+
Este es el flujo exacto que una IA debe seguir para crear una app completa:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# Paso 1: Login
|
|
330
|
+
entexto login
|
|
331
|
+
# → Ingresar email y password
|
|
332
|
+
|
|
333
|
+
# Paso 2: Crear proyecto
|
|
334
|
+
entexto create --name "Mi App Completa" --template fullstack
|
|
335
|
+
# → Anota el UUID del proyecto creado
|
|
336
|
+
|
|
337
|
+
# Paso 3: Desarrollar localmente
|
|
338
|
+
# Crear archivos en una carpeta local:
|
|
339
|
+
mkdir mi-app && cd mi-app
|
|
340
|
+
|
|
341
|
+
# index.html - página principal
|
|
342
|
+
# app.js - lógica de la aplicación
|
|
343
|
+
# styles.css - estilos
|
|
344
|
+
|
|
345
|
+
# Paso 4: Deploy
|
|
346
|
+
entexto deploy --id <uuid> --dir . --publish
|
|
347
|
+
|
|
348
|
+
# Paso 5: Configurar dominio (opcional)
|
|
349
|
+
entexto domain add miapp.com --project <project_id>
|
|
350
|
+
# O crear subdominio:
|
|
351
|
+
entexto domain subdomain
|
|
352
|
+
|
|
353
|
+
# Paso 6: Crear API backend (opcional)
|
|
354
|
+
entexto api create
|
|
355
|
+
# → Nombre: "mi-app-api"
|
|
356
|
+
# → Anota el slug y API Key
|
|
357
|
+
|
|
358
|
+
# Paso 7: Crear colecciones de datos
|
|
359
|
+
entexto api collection mi-app-api
|
|
360
|
+
# → Nombre: "usuarios"
|
|
361
|
+
|
|
362
|
+
entexto api collection mi-app-api
|
|
363
|
+
# → Nombre: "datos"
|
|
364
|
+
|
|
365
|
+
# Paso 8: Insertar datos iniciales
|
|
366
|
+
entexto api insert mi-app-api
|
|
367
|
+
# → Seleccionar colección → Escribir JSON
|
|
368
|
+
|
|
369
|
+
# Paso 9: Agregar Live SDK al proyecto (si necesita tiempo real)
|
|
370
|
+
# En index.html agregar:
|
|
371
|
+
# <script src="/_entexto/live.js"></script>
|
|
372
|
+
# Usar sala.escuchar/emitir para comunicación
|
|
373
|
+
|
|
374
|
+
# Paso 10: Re-deploy con cambios finales
|
|
375
|
+
entexto deploy --id <uuid> --dir . --publish
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Ejemplo: App de Chat en Tiempo Real
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Login
|
|
382
|
+
entexto login
|
|
383
|
+
|
|
384
|
+
# Crear proyecto
|
|
385
|
+
entexto create --name "Chat App" --template basic
|
|
386
|
+
|
|
387
|
+
# Crear carpeta local
|
|
388
|
+
mkdir chat-app && cd chat-app
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Crear `index.html`:
|
|
392
|
+
```html
|
|
393
|
+
<!DOCTYPE html>
|
|
394
|
+
<html lang="es">
|
|
395
|
+
<head>
|
|
396
|
+
<meta charset="UTF-8">
|
|
397
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
398
|
+
<title>Chat App</title>
|
|
399
|
+
<style>
|
|
400
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
401
|
+
body { font-family: system-ui; background: #1a1a2e; color: #eee; display: flex; flex-direction: column; height: 100vh; }
|
|
402
|
+
#chat { flex: 1; overflow-y: auto; padding: 20px; }
|
|
403
|
+
.msg { margin: 8px 0; padding: 10px 15px; background: #16213e; border-radius: 10px; max-width: 80%; }
|
|
404
|
+
.msg .user { color: #e94560; font-weight: bold; font-size: 0.85em; }
|
|
405
|
+
#form { display: flex; padding: 15px; background: #0f3460; }
|
|
406
|
+
#form input { flex: 1; padding: 12px; border: none; border-radius: 8px; background: #1a1a2e; color: #eee; font-size: 1em; }
|
|
407
|
+
#form button { padding: 12px 24px; margin-left: 10px; border: none; border-radius: 8px; background: #e94560; color: #fff; cursor: pointer; font-weight: bold; }
|
|
408
|
+
</style>
|
|
409
|
+
</head>
|
|
410
|
+
<body>
|
|
411
|
+
<div id="chat"></div>
|
|
412
|
+
<form id="form">
|
|
413
|
+
<input id="msg" placeholder="Escribe un mensaje..." autocomplete="off">
|
|
414
|
+
<button type="submit">Enviar</button>
|
|
415
|
+
</form>
|
|
416
|
+
<script>
|
|
417
|
+
const usuario = prompt('Tu nombre:') || 'Anónimo';
|
|
418
|
+
const sala = window.__entexto.live.sala('chat-general', { usuario });
|
|
419
|
+
const chat = document.getElementById('chat');
|
|
420
|
+
|
|
421
|
+
function addMsg(user, text) {
|
|
422
|
+
const div = document.createElement('div');
|
|
423
|
+
div.className = 'msg';
|
|
424
|
+
div.innerHTML = '<span class="user">' + user + '</span><br>' + text;
|
|
425
|
+
chat.appendChild(div);
|
|
426
|
+
chat.scrollTop = chat.scrollHeight;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
sala.escuchar('mensaje', (data, remitente) => {
|
|
430
|
+
addMsg(remitente.usuario || '?', data.texto);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
sala.onEntra((u) => addMsg('Sistema', u + ' se unió al chat'));
|
|
434
|
+
sala.onSale((u) => addMsg('Sistema', u + ' salió del chat'));
|
|
435
|
+
|
|
436
|
+
document.getElementById('form').addEventListener('submit', (e) => {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
const input = document.getElementById('msg');
|
|
439
|
+
const texto = input.value.trim();
|
|
440
|
+
if (!texto) return;
|
|
441
|
+
sala.emitir('mensaje', { texto });
|
|
442
|
+
addMsg(usuario, texto);
|
|
443
|
+
input.value = '';
|
|
444
|
+
});
|
|
445
|
+
</script>
|
|
446
|
+
</body>
|
|
447
|
+
</html>
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
# Deploy y publicar
|
|
452
|
+
entexto deploy --id <uuid> --dir . --publish
|
|
453
|
+
|
|
454
|
+
# Listo! Chat funcionando en https://entexto.com/p/<uuid>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Ejemplo: App con API + Base de Datos
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
# Crear API
|
|
461
|
+
entexto api create
|
|
462
|
+
# Nombre: "tienda-api"
|
|
463
|
+
|
|
464
|
+
# Crear colecciones
|
|
465
|
+
entexto api collection tienda-api
|
|
466
|
+
# → "productos"
|
|
467
|
+
|
|
468
|
+
entexto api collection tienda-api
|
|
469
|
+
# → "pedidos"
|
|
470
|
+
|
|
471
|
+
# Insertar productos
|
|
472
|
+
entexto api insert tienda-api
|
|
473
|
+
# Colección: productos
|
|
474
|
+
# { "nombre": "Laptop", "precio": 999, "stock": 10 }
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Usar desde el frontend:
|
|
478
|
+
```javascript
|
|
479
|
+
const API_BASE = 'https://api.entexto.com/v1/api/projects/tienda-api';
|
|
480
|
+
const API_KEY = 'tu-api-key';
|
|
481
|
+
|
|
482
|
+
// Listar productos
|
|
483
|
+
const res = await fetch(`${API_BASE}/data/productos`, {
|
|
484
|
+
headers: { 'X-API-Key': API_KEY }
|
|
485
|
+
});
|
|
486
|
+
const { documents } = await res.json();
|
|
487
|
+
|
|
488
|
+
// Crear pedido
|
|
489
|
+
await fetch(`${API_BASE}/data/pedidos`, {
|
|
490
|
+
method: 'POST',
|
|
491
|
+
headers: {
|
|
492
|
+
'Content-Type': 'application/json',
|
|
493
|
+
'X-API-Key': API_KEY
|
|
494
|
+
},
|
|
495
|
+
body: JSON.stringify({
|
|
496
|
+
data: { producto_id: '...', cantidad: 1, total: 999 }
|
|
497
|
+
})
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## 10. Otros Comandos Útiles
|
|
504
|
+
|
|
505
|
+
### Listar proyectos
|
|
506
|
+
```bash
|
|
507
|
+
entexto projects
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Descargar un proyecto
|
|
511
|
+
```bash
|
|
512
|
+
entexto pull --id <uuid> --dir ./descarga
|
|
513
|
+
entexto clone --id <uuid>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Sincronización bidireccional (live)
|
|
517
|
+
```bash
|
|
518
|
+
entexto sync --id <uuid> --dir .
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Exponer servidor local
|
|
522
|
+
```bash
|
|
523
|
+
entexto tunnel 3000
|
|
524
|
+
```
|
|
525
|
+
Tu servidor local en `localhost:3000` será accesible públicamente.
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 11. Estructura de Archivos de un Proyecto Típico
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
mi-proyecto/
|
|
533
|
+
├── index.html # Página principal (requerido)
|
|
534
|
+
├── app.js # Lógica principal
|
|
535
|
+
├── styles.css # Estilos
|
|
536
|
+
├── assets/ # Imágenes, fuentes, etc.
|
|
537
|
+
│ ├── logo.png
|
|
538
|
+
│ └── favicon.ico
|
|
539
|
+
└── server/ # Carpeta privada (no se sirve públicamente)
|
|
540
|
+
├── game-logic.js # Lógica del servidor
|
|
541
|
+
└── hooks.js # Hooks de eventos
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
> **Nota**: La carpeta `server/` es privada — los archivos dentro no son accesibles desde el navegador pero sí desde la lógica del servidor (VFS).
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## 12. Sistema de Archivos Virtual (VFS)
|
|
549
|
+
|
|
550
|
+
Los proyectos publicados tienen acceso al VFS para leer/escribir archivos desde JavaScript:
|
|
551
|
+
|
|
552
|
+
```javascript
|
|
553
|
+
// Leer archivo
|
|
554
|
+
const resp = await fetch('/_entexto/api/fs/leer', {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
headers: { 'Content-Type': 'application/json' },
|
|
557
|
+
body: JSON.stringify({ ruta: 'datos/config.json' })
|
|
558
|
+
});
|
|
559
|
+
const { contenido } = await resp.json();
|
|
560
|
+
|
|
561
|
+
// Escribir archivo
|
|
562
|
+
await fetch('/_entexto/api/fs/escribir', {
|
|
563
|
+
method: 'POST',
|
|
564
|
+
headers: { 'Content-Type': 'application/json' },
|
|
565
|
+
body: JSON.stringify({ ruta: 'datos/config.json', contenido: '{"key":"value"}' })
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// Listar archivos
|
|
569
|
+
const list = await fetch('/_entexto/api/fs/listar', {
|
|
570
|
+
method: 'POST',
|
|
571
|
+
headers: { 'Content-Type': 'application/json' },
|
|
572
|
+
body: JSON.stringify({ ruta: '.' })
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Eliminar archivo
|
|
576
|
+
await fetch('/_entexto/api/fs/eliminar', {
|
|
577
|
+
method: 'POST',
|
|
578
|
+
headers: { 'Content-Type': 'application/json' },
|
|
579
|
+
body: JSON.stringify({ ruta: 'datos/temp.txt' })
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// Verificar si existe
|
|
583
|
+
const exists = await fetch('/_entexto/api/fs/existe', {
|
|
584
|
+
method: 'POST',
|
|
585
|
+
headers: { 'Content-Type': 'application/json' },
|
|
586
|
+
body: JSON.stringify({ ruta: 'datos/config.json' })
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
## 13. Seguridad
|
|
593
|
+
|
|
594
|
+
### Autenticación
|
|
595
|
+
- **CLI**: JWT almacenado localmente en `~/.entexto/config.json`
|
|
596
|
+
- **API Platform**: API Key (header `X-API-Key`) o JWT
|
|
597
|
+
- **Dominios**: Verificación DNS obligatoria antes de servir contenido
|
|
598
|
+
|
|
599
|
+
### Buenas prácticas
|
|
600
|
+
- Nunca expongas tu API Key en código del frontend público
|
|
601
|
+
- Usa JWT para operaciones sensibles
|
|
602
|
+
- La carpeta `server/` es privada y no se sirve al público
|
|
603
|
+
- Los tokens de TURN tienen TTL de 24 horas
|
|
604
|
+
- Las credenciales TURN usan HMAC-SHA1
|
|
605
|
+
|
|
606
|
+
### Endpoints protegidos
|
|
607
|
+
Todos los endpoints de CLI, proyectos, y dominios requieren autenticación JWT. El middleware verifica:
|
|
608
|
+
1. Header `Authorization: Bearer <token>`
|
|
609
|
+
2. Token válido y no expirado
|
|
610
|
+
3. Usuario activo en la base de datos
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## 14. Referencia Rápida de Comandos
|
|
615
|
+
|
|
616
|
+
| Comando | Descripción |
|
|
617
|
+
|---------|-------------|
|
|
618
|
+
| `entexto login` | Iniciar sesión |
|
|
619
|
+
| `entexto logout` | Cerrar sesión |
|
|
620
|
+
| `entexto whoami` | Ver usuario actual |
|
|
621
|
+
| `entexto create` | Crear nuevo proyecto |
|
|
622
|
+
| `entexto projects` | Listar proyectos |
|
|
623
|
+
| `entexto deploy` | Subir archivos |
|
|
624
|
+
| `entexto publish` | Publicar proyecto |
|
|
625
|
+
| `entexto pull` | Descargar proyecto |
|
|
626
|
+
| `entexto clone` | Clonar proyecto |
|
|
627
|
+
| `entexto sync` | Sincronización bidireccional |
|
|
628
|
+
| `entexto tunnel` | Exponer servidor local |
|
|
629
|
+
| `entexto domain list` | Ver dominios |
|
|
630
|
+
| `entexto domain add` | Agregar dominio |
|
|
631
|
+
| `entexto domain subdomain` | Crear subdominio |
|
|
632
|
+
| `entexto domain verify` | Verificar DNS |
|
|
633
|
+
| `entexto api list` | Listar APIs |
|
|
634
|
+
| `entexto api create` | Crear nueva API |
|
|
635
|
+
| `entexto api info` | Ver detalle de API |
|
|
636
|
+
| `entexto api collection` | Crear colección |
|
|
637
|
+
| `entexto api insert` | Insertar documento |
|
|
638
|
+
| `entexto api query` | Consultar documentos |
|
|
639
|
+
| `entexto live` | Guía del Live SDK |
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## 15. Resolución de Problemas
|
|
644
|
+
|
|
645
|
+
| Problema | Solución |
|
|
646
|
+
|----------|----------|
|
|
647
|
+
| "No has iniciado sesión" | Ejecuta `entexto login` |
|
|
648
|
+
| Deploy falla con timeout | Usa `--full` para forzar subida completa |
|
|
649
|
+
| Dominio no se verifica | Espera propagación DNS (puede tardar hasta 48h) |
|
|
650
|
+
| API Key inválida | Revisa con `entexto api info <slug>` |
|
|
651
|
+
| Live SDK no funciona | Asegúrate de que el proyecto esté publicado |
|
|
652
|
+
| TURN no conecta | Verifica que el servidor TURN esté activo (PM2 id=2) |
|
|
653
|
+
| Archivos no aparecen | Verifica que no estén en carpeta `server/` (privada) |
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
657
|
+
*Generado por entexto-cli v2.0.0 — https://entexto.com*
|