s3-client-dtb 0.0.1 → 1.0.2
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/README.md +532 -148
- package/dist/client/index.d.ts +74 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +125 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/local-storage-client.d.ts +97 -0
- package/dist/client/local-storage-client.d.ts.map +1 -0
- package/dist/client/local-storage-client.js +108 -0
- package/dist/client/local-storage-client.js.map +1 -0
- package/dist/client/storage-client.d.ts +28 -3
- package/dist/client/storage-client.d.ts.map +1 -1
- package/dist/client/storage-client.js +270 -27
- package/dist/client/storage-client.js.map +1 -1
- package/dist/config/config-reader.d.ts +67 -0
- package/dist/config/config-reader.d.ts.map +1 -0
- package/dist/config/config-reader.js +201 -0
- package/dist/config/config-reader.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -4
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# s3-client-dtb
|
|
2
2
|
|
|
3
|
-
Paquete de almacenamiento de archivos para Node.js y NestJS.
|
|
3
|
+
Paquete de almacenamiento de archivos para Node.js y NestJS.
|
|
4
|
+
|
|
5
|
+
## Servidor previsto (modo remoto)
|
|
6
|
+
|
|
7
|
+
En modo **cliente** (`StorageClient`) o **online**, este paquete está pensado para hablar con el backend ejecutable **[s3-back-dtb](https://www.npmjs.com/package/s3-back-dtb)** en npm. Instálalo en el servidor, configura las variables de entorno compartidas (`CLIENT_SECRET`, `TOTP_SECRET`, etc.) y usa la misma URL en `STORAGE_BASE_URL`. En modo local (`StorageCore` / **offline**) no necesitas ese servidor.
|
|
4
8
|
|
|
5
9
|
## 🎯 Dos Modos de Uso
|
|
6
10
|
|
|
@@ -13,7 +17,7 @@ Almacena archivos directamente en el sistema de archivos de tu servidor. **Ideal
|
|
|
13
17
|
- Cuando no necesitas un backend separado
|
|
14
18
|
|
|
15
19
|
### 🌐 Modo Cliente (StorageClient)
|
|
16
|
-
Se conecta a un backend remoto mediante HTTP. **Ideal para
|
|
20
|
+
Se conecta a un backend remoto mediante HTTP. **Ideal para:**
|
|
17
21
|
- Producción con backend separado
|
|
18
22
|
- Múltiples aplicaciones compartiendo almacenamiento
|
|
19
23
|
- Escalabilidad y distribución
|
|
@@ -22,7 +26,10 @@ Se conecta a un backend remoto mediante HTTP. **Ideal para:
|
|
|
22
26
|
|
|
23
27
|
- ✅ Almacenamiento local de archivos (StorageCore)
|
|
24
28
|
- ✅ Cliente HTTP para backend remoto (StorageClient)
|
|
25
|
-
- ✅ Autenticación con
|
|
29
|
+
- ✅ Autenticación con JWT y TOTP (Time-based OTP)
|
|
30
|
+
- ✅ Generación automática de tokens con CLIENT_SECRET + TOTP
|
|
31
|
+
- ✅ Configuración mediante variables de entorno
|
|
32
|
+
- ✅ Tiempos de expiración configurables por cliente
|
|
26
33
|
- ✅ Validación de tipos MIME
|
|
27
34
|
- ✅ Límites de tamaño de archivo
|
|
28
35
|
- ✅ Sanitización de rutas (protección contra path traversal)
|
|
@@ -47,13 +54,88 @@ npm install @nestjs/common
|
|
|
47
54
|
|
|
48
55
|
### Elegir el Modo Correcto
|
|
49
56
|
|
|
50
|
-
**¿Necesitas almacenar archivos localmente?** → Usa `StorageCore`
|
|
51
|
-
**¿Necesitas conectarte a un backend remoto?** → Usa `StorageClient`
|
|
57
|
+
**¿Necesitas almacenar archivos localmente?** → Usa `StorageCore` o modo `offline`
|
|
58
|
+
**¿Necesitas conectarte a un backend remoto?** → Usa `StorageClient` o modo `online`
|
|
52
59
|
|
|
53
60
|
Ambos tienen la **misma API**, solo cambia la inicialización.
|
|
54
61
|
|
|
55
62
|
---
|
|
56
63
|
|
|
64
|
+
## 🔄 Modo Offline/Online (Recomendado para desarrollo)
|
|
65
|
+
|
|
66
|
+
El paquete soporta un **modo automático** que detecta si estás en desarrollo (offline) o producción (online) basándose en la variable de entorno `STORAGE_MODE`.
|
|
67
|
+
|
|
68
|
+
### ¿Por qué usar esto?
|
|
69
|
+
|
|
70
|
+
- **En desarrollo**: No necesitas tener un servidor de almacenamiento corriendo
|
|
71
|
+
- **En producción**: Usa el servidor remoto sin cambiar código
|
|
72
|
+
- **Mismo código**: La API es idéntica, solo cambian las variables de entorno
|
|
73
|
+
|
|
74
|
+
### Configuración Rápida
|
|
75
|
+
|
|
76
|
+
**Para desarrollo (offline):**
|
|
77
|
+
```bash
|
|
78
|
+
# .env
|
|
79
|
+
STORAGE_MODE=offline
|
|
80
|
+
STORAGE_ROOT_PATH=./uploads
|
|
81
|
+
STORAGE_MAX_FILE_SIZE=50mb # Opcional
|
|
82
|
+
STORAGE_ALLOWED_MIME_TYPES=image/*,video/* # Opcional
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Para producción (online):**
|
|
86
|
+
```bash
|
|
87
|
+
# .env
|
|
88
|
+
STORAGE_MODE=online
|
|
89
|
+
STORAGE_BASE_URL=https://storage.example.com
|
|
90
|
+
STORAGE_CLIENT_ID=mi-app
|
|
91
|
+
STORAGE_CLIENT_NAME=Mi Aplicacion
|
|
92
|
+
STORAGE_PERMISSIONS=read,write,delete
|
|
93
|
+
STORAGE_ALLOWED_PATHS=*
|
|
94
|
+
STORAGE_CLIENT_SECRET=tu-clave-secreta...
|
|
95
|
+
STORAGE_TOTP_SECRET=tu-semilla-totp...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Uso en Código
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import 'dotenv/config';
|
|
102
|
+
import { createClientFromConfig, isOfflineClient } from 's3-client-dtb';
|
|
103
|
+
|
|
104
|
+
// ✅ Detecta automáticamente el modo según STORAGE_MODE
|
|
105
|
+
const client = createClientFromConfig();
|
|
106
|
+
|
|
107
|
+
// La API es idéntica en ambos modos
|
|
108
|
+
const path = await client.uploadFile('images', buffer, 'image/png');
|
|
109
|
+
const stream = await client.downloadFile(path);
|
|
110
|
+
const exists = await client.fileExists(path);
|
|
111
|
+
await client.deleteFile(path);
|
|
112
|
+
|
|
113
|
+
// Si necesitas saber en qué modo estás:
|
|
114
|
+
if (isOfflineClient(client)) {
|
|
115
|
+
console.log('Modo offline - archivos guardados localmente');
|
|
116
|
+
} else {
|
|
117
|
+
console.log('Modo online - usando servidor remoto');
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Funciones Auxiliares
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import {
|
|
125
|
+
createClientFromConfig, // Detecta modo automáticamente
|
|
126
|
+
createOfflineClient, // Forzar modo offline
|
|
127
|
+
createOnlineClient, // Forzar modo online (deprecated, usa createClientFromConfig)
|
|
128
|
+
getStorageMode, // Obtener el modo actual
|
|
129
|
+
isOfflineClient, // Verificar si es cliente offline
|
|
130
|
+
isOnlineClient, // Verificar si es cliente online
|
|
131
|
+
} from 's3-client-dtb';
|
|
132
|
+
|
|
133
|
+
// Obtener el modo configurado
|
|
134
|
+
const mode = getStorageMode(); // 'online' | 'offline'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
57
139
|
## 📁 Modo Local (StorageCore)
|
|
58
140
|
|
|
59
141
|
### Configuración
|
|
@@ -95,51 +177,363 @@ const exists = await storage.fileExists(path);
|
|
|
95
177
|
|
|
96
178
|
## 🌐 Modo Cliente (StorageClient)
|
|
97
179
|
|
|
98
|
-
### Configuración
|
|
180
|
+
### Configuración con Variables de Entorno
|
|
181
|
+
|
|
182
|
+
El cliente se configura mediante variables de entorno. Crea un archivo `.env` basado en `.env.example`:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Modo de almacenamiento (online para servidor remoto)
|
|
186
|
+
STORAGE_MODE=online
|
|
187
|
+
|
|
188
|
+
# Variables requeridas para modo online
|
|
189
|
+
STORAGE_CLIENT_ID=mi-cliente-123
|
|
190
|
+
STORAGE_CLIENT_NAME=Mi Aplicacion
|
|
191
|
+
STORAGE_BASE_URL=http://localhost:3000
|
|
192
|
+
STORAGE_PERMISSIONS=["read","write","delete"]
|
|
193
|
+
STORAGE_ALLOWED_PATHS=["*"]
|
|
194
|
+
STORAGE_CLIENT_SECRET=tu-clave-secreta-compartida-minimo-32-caracteres
|
|
195
|
+
STORAGE_TOTP_SECRET=tu-semilla-totp-compartida-minimo-16-caracteres
|
|
196
|
+
|
|
197
|
+
# Variables opcionales
|
|
198
|
+
STORAGE_TOKEN_EXPIRES_IN=5m
|
|
199
|
+
STORAGE_TOTP_PERIOD=30
|
|
200
|
+
STORAGE_RATE_LIMIT_REQUESTS=1000
|
|
201
|
+
STORAGE_RATE_LIMIT_WINDOW=1h
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**IMPORTANTE**:
|
|
205
|
+
- Los valores de `STORAGE_CLIENT_SECRET` y `STORAGE_TOTP_SECRET` deben ser los mismos que están en las variables de entorno del servidor (`CLIENT_SECRET` y `TOTP_SECRET`).
|
|
206
|
+
- Si el servidor usa `TOTP_PERIOD` distinto de 30, configura `STORAGE_TOTP_PERIOD` con el mismo valor (15–300 segundos).
|
|
207
|
+
- Para `STORAGE_PERMISSIONS` y `STORAGE_ALLOWED_PATHS` puedes usar JSON array (`["read","write","delete"]`) o valores separados por comas (`read,write,delete`).
|
|
208
|
+
|
|
209
|
+
### 🔗 Conectando con el Backend
|
|
210
|
+
|
|
211
|
+
Para que el cliente se conecte correctamente al backend, necesitas asegurarte de que ciertos valores coincidan exactamente entre ambos.
|
|
212
|
+
|
|
213
|
+
#### Checklist de Configuración
|
|
214
|
+
|
|
215
|
+
Antes de usar el cliente, verifica que:
|
|
216
|
+
|
|
217
|
+
- [ ] **`STORAGE_CLIENT_SECRET`** (cliente) = **`CLIENT_SECRET`** (servidor)
|
|
218
|
+
- [ ] **`STORAGE_TOTP_SECRET`** (cliente) = **`TOTP_SECRET`** (servidor)
|
|
219
|
+
- [ ] **`STORAGE_TOTP_PERIOD`** (cliente) = **`TOTP_PERIOD`** (servidor) - Si el servidor usa un valor distinto de 30
|
|
220
|
+
- [ ] **`STORAGE_BASE_URL`** apunta a la URL correcta del servidor (ej: `http://localhost:3000`)
|
|
221
|
+
|
|
222
|
+
#### Ejemplo de Configuración Completa
|
|
223
|
+
|
|
224
|
+
**Backend (s3-package/.env):**
|
|
225
|
+
```env
|
|
226
|
+
CLIENT_SECRET=mi-clave-secreta-compartida-minimo-32-caracteres-1234567890
|
|
227
|
+
TOTP_SECRET=mi-semilla-totp-compartida-minimo-16-caracteres
|
|
228
|
+
TOTP_PERIOD=30
|
|
229
|
+
PORT=3000
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Cliente (tu-app/.env):**
|
|
233
|
+
```env
|
|
234
|
+
STORAGE_CLIENT_ID=mi-cliente-123
|
|
235
|
+
STORAGE_CLIENT_NAME=Mi Aplicacion
|
|
236
|
+
STORAGE_BASE_URL=http://localhost:3000
|
|
237
|
+
STORAGE_PERMISSIONS=["read","write","delete"]
|
|
238
|
+
STORAGE_ALLOWED_PATHS=["*"]
|
|
239
|
+
STORAGE_CLIENT_SECRET=mi-clave-secreta-compartida-minimo-32-caracteres-1234567890
|
|
240
|
+
STORAGE_TOTP_SECRET=mi-semilla-totp-compartida-minimo-16-caracteres
|
|
241
|
+
STORAGE_TOTP_PERIOD=30
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**⚠️ Importante**: Los valores de `CLIENT_SECRET` y `TOTP_SECRET` deben ser **exactamente iguales** en ambos archivos `.env`.
|
|
245
|
+
|
|
246
|
+
#### Verificar la Conexión
|
|
247
|
+
|
|
248
|
+
Una vez configurado, puedes verificar que la conexión funciona correctamente:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import 'dotenv/config';
|
|
252
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
253
|
+
|
|
254
|
+
const client = createClientFromConfig();
|
|
255
|
+
|
|
256
|
+
// Verificar que puedes generar códigos TOTP correctos
|
|
257
|
+
try {
|
|
258
|
+
await client.verifyTotp();
|
|
259
|
+
console.log('✅ Conexión con el backend verificada correctamente');
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error('❌ Error al conectar con el backend:', error);
|
|
262
|
+
// Verifica que los secrets coincidan y que el servidor esté corriendo
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### Solución de Problemas de Conexión
|
|
267
|
+
|
|
268
|
+
**Error: "UNAUTHORIZED" o "TOTP_VERIFY_ERROR"**
|
|
269
|
+
- Verifica que `STORAGE_CLIENT_SECRET` y `STORAGE_TOTP_SECRET` coincidan exactamente con los del servidor
|
|
270
|
+
- Verifica que `STORAGE_TOTP_PERIOD` coincida con `TOTP_PERIOD` del servidor
|
|
271
|
+
- Asegúrate de que el servidor esté corriendo y accesible en `STORAGE_BASE_URL`
|
|
272
|
+
|
|
273
|
+
**Error: "Timeout al conectar con el servidor"**
|
|
274
|
+
- Verifica que `STORAGE_BASE_URL` sea correcta y el servidor esté corriendo
|
|
275
|
+
- Verifica la conectividad de red
|
|
276
|
+
- Aumenta el `timeout` en la configuración manual si es necesario
|
|
277
|
+
|
|
278
|
+
**Error: "FORBIDDEN"**
|
|
279
|
+
- Verifica que los `STORAGE_PERMISSIONS` incluyan la operación que intentas realizar
|
|
280
|
+
- Verifica que el `STORAGE_ALLOWED_PATHS` permita la ruta que intentas usar
|
|
281
|
+
|
|
282
|
+
### Cargar Variables de Entorno
|
|
283
|
+
|
|
284
|
+
Si usas Node.js, puedes usar `dotenv` para cargar el archivo `.env`:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm install dotenv
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import 'dotenv/config';
|
|
292
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
293
|
+
|
|
294
|
+
const client = createClientFromConfig();
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
O cargar manualmente:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { config } from 'dotenv';
|
|
301
|
+
config(); // Carga .env desde el directorio actual
|
|
302
|
+
|
|
303
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
304
|
+
const client = createClientFromConfig();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Crear Cliente desde Variables de Entorno
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
311
|
+
|
|
312
|
+
// Lee las variables de entorno automáticamente
|
|
313
|
+
const client = createClientFromConfig();
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Configuración Manual
|
|
99
317
|
|
|
100
318
|
```typescript
|
|
101
319
|
import { StorageClient } from 's3-client-dtb';
|
|
320
|
+
import type { ClientConfig } from 's3-client-dtb';
|
|
321
|
+
|
|
322
|
+
const clientConfig: ClientConfig = {
|
|
323
|
+
clientId: 'mi-cliente-123',
|
|
324
|
+
name: 'Mi Aplicacion',
|
|
325
|
+
permissions: ['read', 'write', 'delete'],
|
|
326
|
+
allowedPaths: ['*'],
|
|
327
|
+
rateLimit: {
|
|
328
|
+
requests: 1000,
|
|
329
|
+
window: '1h'
|
|
330
|
+
},
|
|
331
|
+
tokenExpiresIn: '5m'
|
|
332
|
+
};
|
|
102
333
|
|
|
103
|
-
// ✅ Configuración simple - Solo necesitas URL y API key
|
|
104
334
|
const client = new StorageClient({
|
|
105
|
-
baseUrl: 'https://api.midominio.com',
|
|
106
|
-
|
|
107
|
-
|
|
335
|
+
baseUrl: 'https://api.midominio.com',
|
|
336
|
+
clientConfig,
|
|
337
|
+
clientSecret: 'tu-clave-secreta-compartida-minimo-32-caracteres',
|
|
338
|
+
totpSecret: 'tu-semilla-totp-compartida-minimo-16-caracteres',
|
|
339
|
+
totpPeriodSeconds: 30, // opcional; debe coincidir con TOTP_PERIOD del servidor
|
|
108
340
|
});
|
|
109
341
|
```
|
|
110
342
|
|
|
343
|
+
### Autenticación Automática
|
|
344
|
+
|
|
345
|
+
El cliente genera tokens automáticamente usando TOTP:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
349
|
+
|
|
350
|
+
const client = createClientFromConfig();
|
|
351
|
+
|
|
352
|
+
// El cliente genera tokens automáticamente usando CLIENT_SECRET + TOTP
|
|
353
|
+
// No necesitas llamar ningún método de autenticación manualmente
|
|
354
|
+
|
|
355
|
+
// Opcional: Verificar que puedes generar códigos TOTP correctos
|
|
356
|
+
await client.verifyTotp();
|
|
357
|
+
|
|
358
|
+
// Ahora puedes usar el cliente normalmente
|
|
359
|
+
// Los tokens se generan y renuevan automáticamente
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Variables de Entorno
|
|
363
|
+
|
|
364
|
+
#### Requeridas
|
|
365
|
+
|
|
366
|
+
- `STORAGE_CLIENT_ID` (string): Identificador único del cliente
|
|
367
|
+
- `STORAGE_CLIENT_NAME` (string): Nombre descriptivo de la aplicación
|
|
368
|
+
- `STORAGE_BASE_URL` (string): URL del backend (ej: `http://localhost:3000`)
|
|
369
|
+
- `STORAGE_PERMISSIONS` (string): Permisos del cliente. Puede ser JSON array (`["read","write","delete"]`) o valores separados por comas (`read,write,delete`)
|
|
370
|
+
- `STORAGE_ALLOWED_PATHS` (string): Paths permitidos. Puede ser JSON array (`["*"]` o `["images/*","documents/*"]`) o valores separados por comas (`*` o `images/*,documents/*`)
|
|
371
|
+
- `STORAGE_CLIENT_SECRET` (string): Clave secreta compartida con el servidor (mínimo 32 caracteres)
|
|
372
|
+
- `STORAGE_TOTP_SECRET` (string): Semilla TOTP compartida con el servidor (mínimo 16 caracteres)
|
|
373
|
+
|
|
374
|
+
#### Opcionales
|
|
375
|
+
|
|
376
|
+
- `STORAGE_TOKEN_EXPIRES_IN` (string): Duración del token JWT (ej: `"5m"`, `"1h"`, `"30m"`). Si no se especifica, se usa el default del servidor (`TOKEN_EXPIRES_IN`)
|
|
377
|
+
- `STORAGE_TOTP_PERIOD` (number): Período TOTP en segundos (15–300, default 30). Debe coincidir con `TOTP_PERIOD` del servidor.
|
|
378
|
+
- `STORAGE_RATE_LIMIT_REQUESTS` (number): Número de peticiones permitidas (solo se usa si también defines `STORAGE_RATE_LIMIT_WINDOW`)
|
|
379
|
+
- `STORAGE_RATE_LIMIT_WINDOW` (string): Ventana de tiempo para el rate limit (ej: `"1h"`, `"30m"`). Solo se usa si también defines `STORAGE_RATE_LIMIT_REQUESTS`
|
|
380
|
+
|
|
381
|
+
#### Tabla de Referencia Rápida
|
|
382
|
+
|
|
383
|
+
| Variable | Requerida | Tipo | Default | Descripción |
|
|
384
|
+
|----------|-----------|------|---------|-------------|
|
|
385
|
+
| `STORAGE_CLIENT_ID` | ✅ | string | - | Identificador único del cliente |
|
|
386
|
+
| `STORAGE_CLIENT_NAME` | ✅ | string | - | Nombre descriptivo de la aplicación |
|
|
387
|
+
| `STORAGE_BASE_URL` | ✅ | string | - | URL del backend (ej: `http://localhost:3000`) |
|
|
388
|
+
| `STORAGE_PERMISSIONS` | ✅ | string | - | Permisos: JSON array o comma-separated (`["read","write","delete"]` o `read,write,delete`) |
|
|
389
|
+
| `STORAGE_ALLOWED_PATHS` | ✅ | string | - | Paths permitidos: JSON array o comma-separated (`["*"]` o `*`) |
|
|
390
|
+
| `STORAGE_CLIENT_SECRET` | ✅ | string | - | Clave secreta compartida (mínimo 32 caracteres). **Debe coincidir con `CLIENT_SECRET` del servidor** |
|
|
391
|
+
| `STORAGE_TOTP_SECRET` | ✅ | string | - | Semilla TOTP compartida (mínimo 16 caracteres). **Debe coincidir con `TOTP_SECRET` del servidor** |
|
|
392
|
+
| `STORAGE_TOKEN_EXPIRES_IN` | ❌ | string | `5m` | Duración del token JWT (`30s`, `5m`, `1h`, `7d`) |
|
|
393
|
+
| `STORAGE_TOTP_PERIOD` | ❌ | number | `30` | Período TOTP en segundos (15-300). **Debe coincidir con `TOTP_PERIOD` del servidor** |
|
|
394
|
+
| `STORAGE_RATE_LIMIT_REQUESTS` | ❌ | number | - | Número de peticiones permitidas (requiere `STORAGE_RATE_LIMIT_WINDOW`) |
|
|
395
|
+
| `STORAGE_RATE_LIMIT_WINDOW` | ❌ | string | - | Ventana de tiempo para rate limit (`1h`, `30m`, `7d`) |
|
|
396
|
+
|
|
111
397
|
### Ejemplos de baseUrl
|
|
112
398
|
|
|
113
399
|
```typescript
|
|
114
400
|
// Con dominio
|
|
115
|
-
new StorageClient({
|
|
401
|
+
new StorageClient({
|
|
402
|
+
baseUrl: 'https://api.midominio.com',
|
|
403
|
+
clientConfig
|
|
404
|
+
});
|
|
116
405
|
|
|
117
406
|
// Con IP directa
|
|
118
|
-
new StorageClient({
|
|
407
|
+
new StorageClient({
|
|
408
|
+
baseUrl: 'http://192.168.1.100:3000',
|
|
409
|
+
clientConfig
|
|
410
|
+
});
|
|
119
411
|
|
|
120
412
|
// Con puerto personalizado
|
|
121
|
-
new StorageClient({
|
|
413
|
+
new StorageClient({
|
|
414
|
+
baseUrl: 'http://localhost:8080',
|
|
415
|
+
clientConfig
|
|
416
|
+
});
|
|
122
417
|
```
|
|
123
418
|
|
|
124
419
|
### Uso Completo
|
|
125
420
|
|
|
126
421
|
```typescript
|
|
127
|
-
import
|
|
422
|
+
import 'dotenv/config'; // Cargar variables de entorno
|
|
423
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
424
|
+
import {
|
|
425
|
+
FileNotFoundError,
|
|
426
|
+
UploadError,
|
|
427
|
+
StorageError
|
|
428
|
+
} from 's3-client-dtb';
|
|
128
429
|
import { readFileSync } from 'fs';
|
|
129
430
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
apiKey: 'sk_live_xxx'
|
|
133
|
-
});
|
|
431
|
+
// Inicializar cliente desde variables de entorno
|
|
432
|
+
const client = createClientFromConfig();
|
|
134
433
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
434
|
+
// Opcional: Verificar que la configuración TOTP es correcta
|
|
435
|
+
try {
|
|
436
|
+
await client.verifyTotp();
|
|
437
|
+
console.log('✅ Cliente configurado correctamente');
|
|
438
|
+
} catch (error) {
|
|
439
|
+
console.error('❌ Error en configuración:', error);
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Ejemplo: Subir un archivo
|
|
444
|
+
try {
|
|
445
|
+
const buffer = readFileSync('./mi-imagen.jpg');
|
|
446
|
+
const path = await client.uploadFile(
|
|
447
|
+
'images/photo.jpg', // Ruta donde se guardará
|
|
448
|
+
buffer, // Buffer del archivo
|
|
449
|
+
'image/jpeg' // Tipo MIME
|
|
450
|
+
);
|
|
451
|
+
console.log('✅ Archivo subido en:', path);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (error instanceof UploadError) {
|
|
454
|
+
console.error('❌ Error al subir:', error.message);
|
|
455
|
+
} else {
|
|
456
|
+
console.error('❌ Error inesperado:', error);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Ejemplo: Descargar un archivo
|
|
461
|
+
try {
|
|
462
|
+
const stream = await client.downloadFile('images/photo.jpg');
|
|
463
|
+
|
|
464
|
+
// Convertir stream a buffer si es necesario
|
|
465
|
+
const chunks: Buffer[] = [];
|
|
466
|
+
for await (const chunk of stream) {
|
|
467
|
+
chunks.push(chunk);
|
|
468
|
+
}
|
|
469
|
+
const downloadedBuffer = Buffer.concat(chunks);
|
|
470
|
+
console.log('✅ Archivo descargado:', downloadedBuffer.length, 'bytes');
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (error instanceof FileNotFoundError) {
|
|
473
|
+
console.error('❌ Archivo no encontrado');
|
|
474
|
+
} else {
|
|
475
|
+
console.error('❌ Error al descargar:', error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Ejemplo: Obtener metadata
|
|
480
|
+
try {
|
|
481
|
+
const metadata = await client.getFileMetadata('images/photo.jpg');
|
|
482
|
+
console.log('Metadata:', {
|
|
483
|
+
nombre: metadata.name,
|
|
484
|
+
tamaño: metadata.size,
|
|
485
|
+
tipo: metadata.mimeType,
|
|
486
|
+
creado: metadata.createdAt
|
|
487
|
+
});
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error('❌ Error al obtener metadata:', error);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Ejemplo: Listar archivos
|
|
493
|
+
try {
|
|
494
|
+
const { files, total } = await client.listFiles('images', {
|
|
495
|
+
limit: 10, // Máximo 10 archivos
|
|
496
|
+
offset: 0, // Desde el inicio
|
|
497
|
+
prefix: 'photo' // Solo archivos que empiecen con "photo"
|
|
498
|
+
});
|
|
499
|
+
console.log(`✅ Encontrados ${files.length} de ${total} archivos`);
|
|
500
|
+
files.forEach(file => {
|
|
501
|
+
console.log(` - ${file.name} (${file.size} bytes)`);
|
|
502
|
+
});
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.error('❌ Error al listar archivos:', error);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Ejemplo: Verificar existencia
|
|
508
|
+
const exists = await client.fileExists('images/photo.jpg');
|
|
509
|
+
console.log('Archivo existe:', exists);
|
|
510
|
+
|
|
511
|
+
// Ejemplo: Eliminar archivo
|
|
512
|
+
try {
|
|
513
|
+
await client.deleteFile('images/photo.jpg');
|
|
514
|
+
console.log('✅ Archivo eliminado');
|
|
515
|
+
} catch (error) {
|
|
516
|
+
if (error instanceof FileNotFoundError) {
|
|
517
|
+
console.error('❌ Archivo no encontrado para eliminar');
|
|
518
|
+
} else {
|
|
519
|
+
console.error('❌ Error al eliminar:', error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Gestión de Tokens
|
|
525
|
+
|
|
526
|
+
El cliente genera tokens automáticamente usando TOTP:
|
|
527
|
+
|
|
528
|
+
- **Generación automática**: El cliente genera tokens JWT usando `CLIENT_SECRET + TOTP_CODE`
|
|
529
|
+
- **TOTP dinámico**: El código TOTP cambia cada 30 segundos, proporcionando seguridad adicional
|
|
530
|
+
- **Renovación automática**: Los tokens se regeneran automáticamente cuando expiran o cuando cambia el código TOTP
|
|
531
|
+
- **Caché inteligente**: Los tokens se cachean y se regeneran solo cuando es necesario
|
|
532
|
+
- **Errores 401**: El cliente regenera el token automáticamente si recibe un 401
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
// El cliente siempre está listo para usar, genera tokens automáticamente
|
|
536
|
+
// No necesitas métodos de autenticación manual
|
|
143
537
|
```
|
|
144
538
|
|
|
145
539
|
---
|
|
@@ -151,20 +545,23 @@ Como ambos tienen la misma API, cambiar de modo es **súper simple**:
|
|
|
151
545
|
### Opción 1: Variable de Entorno
|
|
152
546
|
|
|
153
547
|
```typescript
|
|
154
|
-
import { StorageCore,
|
|
548
|
+
import { StorageCore, createClientFromConfig } from 's3-client-dtb';
|
|
155
549
|
|
|
156
550
|
// Usar variable de entorno para decidir el modo
|
|
157
551
|
const useRemote = process.env.STORAGE_MODE === 'remote';
|
|
158
552
|
|
|
159
553
|
const storage = useRemote
|
|
160
|
-
?
|
|
161
|
-
baseUrl: process.env.STORAGE_URL!,
|
|
162
|
-
apiKey: process.env.STORAGE_API_KEY!
|
|
163
|
-
})
|
|
554
|
+
? createClientFromConfig() // Lee variables de entorno automáticamente
|
|
164
555
|
: new StorageCore({
|
|
165
556
|
rootPath: process.env.STORAGE_PATH || './uploads'
|
|
166
557
|
});
|
|
167
558
|
|
|
559
|
+
// Si es remoto, el cliente genera tokens automáticamente
|
|
560
|
+
// Opcional: Verificar TOTP
|
|
561
|
+
if (useRemote && storage instanceof StorageClient) {
|
|
562
|
+
await storage.verifyTotp();
|
|
563
|
+
}
|
|
564
|
+
|
|
168
565
|
// El resto del código es idéntico
|
|
169
566
|
await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
|
|
170
567
|
```
|
|
@@ -172,15 +569,18 @@ await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
|
|
|
172
569
|
### Opción 2: Factory Function
|
|
173
570
|
|
|
174
571
|
```typescript
|
|
175
|
-
import { StorageCore,
|
|
176
|
-
|
|
177
|
-
function createStorage() {
|
|
178
|
-
if (process.env.STORAGE_URL
|
|
179
|
-
// Modo remoto
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
572
|
+
import { StorageCore, createClientFromConfig } from 's3-client-dtb';
|
|
573
|
+
|
|
574
|
+
async function createStorage() {
|
|
575
|
+
if (process.env.STORAGE_URL) {
|
|
576
|
+
// Modo remoto - usa variables de entorno
|
|
577
|
+
const client = createClientFromConfig();
|
|
578
|
+
|
|
579
|
+
// El cliente genera tokens automáticamente
|
|
580
|
+
// Opcional: Verificar TOTP
|
|
581
|
+
await client.verifyTotp();
|
|
582
|
+
|
|
583
|
+
return client;
|
|
184
584
|
}
|
|
185
585
|
|
|
186
586
|
// Modo local (por defecto)
|
|
@@ -189,35 +589,12 @@ function createStorage() {
|
|
|
189
589
|
});
|
|
190
590
|
}
|
|
191
591
|
|
|
192
|
-
const storage = createStorage();
|
|
592
|
+
const storage = await createStorage();
|
|
193
593
|
// Usar normalmente
|
|
194
594
|
await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
|
|
195
595
|
```
|
|
196
596
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
import { StorageCore, StorageClient } from 's3-client-dtb';
|
|
201
|
-
import type { FileMetadata, ListFilesOptions } from 's3-client-dtb';
|
|
202
|
-
|
|
203
|
-
// Interfaz común para ambos modos
|
|
204
|
-
interface Storage {
|
|
205
|
-
uploadFile(path: string, buffer: Buffer, mimeType: string, prefix?: string): Promise<string>;
|
|
206
|
-
downloadFile(path: string): Promise<Readable>;
|
|
207
|
-
deleteFile(path: string): Promise<void>;
|
|
208
|
-
getFileMetadata(path: string): Promise<FileMetadata>;
|
|
209
|
-
listFiles(directoryPath?: string, options?: ListFilesOptions): Promise<{files: FileMetadata[], total: number}>;
|
|
210
|
-
fileExists(path: string): Promise<boolean>;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Crear instancia según configuración
|
|
214
|
-
const storage: Storage = process.env.STORAGE_MODE === 'remote'
|
|
215
|
-
? new StorageClient({ baseUrl: process.env.STORAGE_URL!, apiKey: process.env.STORAGE_API_KEY! })
|
|
216
|
-
: new StorageCore({ rootPath: './uploads' });
|
|
217
|
-
|
|
218
|
-
// Código tipo-seguro
|
|
219
|
-
await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
|
|
220
|
-
```
|
|
597
|
+
---
|
|
221
598
|
|
|
222
599
|
## Uso desde NestJS
|
|
223
600
|
|
|
@@ -282,6 +659,8 @@ export class FilesService {
|
|
|
282
659
|
}
|
|
283
660
|
```
|
|
284
661
|
|
|
662
|
+
---
|
|
663
|
+
|
|
285
664
|
## 📖 API Completa
|
|
286
665
|
|
|
287
666
|
### ⚡ Métodos Comunes (Ambos Modos)
|
|
@@ -325,84 +704,40 @@ interface StorageCoreOptions {
|
|
|
325
704
|
#### StorageClient (Modo Remoto)
|
|
326
705
|
|
|
327
706
|
```typescript
|
|
707
|
+
// Opción 1: Desde variables de entorno (recomendado)
|
|
708
|
+
createClientFromConfig(): StorageClient
|
|
709
|
+
|
|
710
|
+
// Opción 2: Manual
|
|
328
711
|
new StorageClient(options: StorageClientOptions)
|
|
329
712
|
|
|
330
713
|
interface StorageClientOptions {
|
|
331
|
-
baseUrl: string;
|
|
332
|
-
|
|
333
|
-
|
|
714
|
+
baseUrl: string; // URL del backend
|
|
715
|
+
clientConfig: ClientConfig; // Configuración del cliente
|
|
716
|
+
clientSecret: string; // Clave secreta compartida (mínimo 32 caracteres). Debe coincidir con CLIENT_SECRET del servidor
|
|
717
|
+
totpSecret: string; // Semilla TOTP compartida (mínimo 16 caracteres). Debe coincidir con TOTP_SECRET del servidor
|
|
718
|
+
totpPeriodSeconds?: number; // Opcional: Período TOTP en segundos (15-300, default: 30). Debe coincidir con TOTP_PERIOD del servidor
|
|
719
|
+
timeout?: number; // Opcional: timeout en ms (default: 30000)
|
|
334
720
|
}
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
**Nota:** El cliente automáticamente agrega el prefijo `/apifiles` a las URLs y maneja la autenticación con API keys.
|
|
338
|
-
|
|
339
|
-
#### Constructor
|
|
340
721
|
|
|
341
|
-
|
|
342
|
-
|
|
722
|
+
interface ClientConfig {
|
|
723
|
+
clientId: string;
|
|
724
|
+
name: string;
|
|
725
|
+
permissions: ('read' | 'write' | 'delete')[];
|
|
726
|
+
allowedPaths: string[];
|
|
727
|
+
rateLimit?: {
|
|
728
|
+
requests: number;
|
|
729
|
+
window: string;
|
|
730
|
+
};
|
|
731
|
+
tokenExpiresIn?: string; // Opcional: "30m", "1h", "5m", "7d"
|
|
732
|
+
}
|
|
343
733
|
```
|
|
344
734
|
|
|
345
|
-
**
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
|
|
350
|
-
#### Métodos
|
|
351
|
-
|
|
352
|
-
##### `uploadFile(path, buffer, mimeType, prefix?)`
|
|
353
|
-
|
|
354
|
-
Sube un archivo al almacenamiento.
|
|
355
|
-
|
|
356
|
-
- `path` (string): Ruta relativa donde se guardará el archivo
|
|
357
|
-
- `buffer` (Buffer): Contenido del archivo
|
|
358
|
-
- `mimeType` (string): Tipo MIME del archivo
|
|
359
|
-
- `prefix` (string, opcional): Prefijo para el nombre del archivo (se genera un UUID)
|
|
360
|
-
|
|
361
|
-
**Retorna:** `Promise<string>` - Ruta relativa del archivo guardado
|
|
362
|
-
|
|
363
|
-
##### `downloadFile(path)`
|
|
364
|
-
|
|
365
|
-
Descarga un archivo como stream.
|
|
366
|
-
|
|
367
|
-
- `path` (string): Ruta relativa del archivo
|
|
368
|
-
|
|
369
|
-
**Retorna:** `Promise<Readable>` - Stream de Node.js
|
|
370
|
-
|
|
371
|
-
##### `deleteFile(path)`
|
|
372
|
-
|
|
373
|
-
Elimina un archivo.
|
|
374
|
-
|
|
375
|
-
- `path` (string): Ruta relativa del archivo
|
|
376
|
-
|
|
377
|
-
**Retorna:** `Promise<void>`
|
|
378
|
-
|
|
379
|
-
##### `getFileMetadata(path)`
|
|
380
|
-
|
|
381
|
-
Obtiene metadata de un archivo.
|
|
382
|
-
|
|
383
|
-
- `path` (string): Ruta relativa del archivo
|
|
384
|
-
|
|
385
|
-
**Retorna:** `Promise<FileMetadata>`
|
|
386
|
-
|
|
387
|
-
##### `listFiles(directoryPath?, options?)`
|
|
388
|
-
|
|
389
|
-
Lista archivos en un directorio.
|
|
390
|
-
|
|
391
|
-
- `directoryPath` (string, opcional): Ruta del directorio (vacío para raíz)
|
|
392
|
-
- `options` (ListFilesOptions, opcional):
|
|
393
|
-
- `limit` (number): Número máximo de archivos
|
|
394
|
-
- `offset` (number): Offset para paginación
|
|
395
|
-
- `prefix` (string): Filtrar por prefijo en el nombre
|
|
396
|
-
|
|
397
|
-
**Retorna:** `Promise<{ files: FileMetadata[], total: number }>`
|
|
398
|
-
|
|
399
|
-
##### `fileExists(path)`
|
|
400
|
-
|
|
401
|
-
Verifica si un archivo existe.
|
|
735
|
+
**Nota:**
|
|
736
|
+
- El cliente automáticamente agrega el prefijo `/apifiles` a las URLs
|
|
737
|
+
- Genera tokens automáticamente usando `CLIENT_SECRET + TOTP` (no requiere autenticación manual)
|
|
738
|
+
- Regenera tokens automáticamente cuando expiran o cuando cambia el código TOTP
|
|
402
739
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
**Retorna:** `Promise<boolean>`
|
|
740
|
+
---
|
|
406
741
|
|
|
407
742
|
## Tipos
|
|
408
743
|
|
|
@@ -420,32 +755,41 @@ interface FileMetadata {
|
|
|
420
755
|
}
|
|
421
756
|
```
|
|
422
757
|
|
|
758
|
+
### ListFilesOptions
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
interface ListFilesOptions {
|
|
762
|
+
limit?: number;
|
|
763
|
+
offset?: number;
|
|
764
|
+
prefix?: string;
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
423
770
|
## 📋 Comparación Rápida
|
|
424
771
|
|
|
425
772
|
| Característica | StorageCore (Local) | StorageClient (Remoto) |
|
|
426
773
|
|---------------|---------------------|------------------------|
|
|
427
|
-
| **Inicialización** | `new StorageCore({ rootPath })` | `new StorageClient({ baseUrl,
|
|
774
|
+
| **Inicialización** | `new StorageCore({ rootPath })` | `createClientFromConfig()` o `new StorageClient({ baseUrl, clientConfig })` |
|
|
428
775
|
| **Almacenamiento** | Sistema de archivos local | Backend remoto vía HTTP |
|
|
429
776
|
| **API** | ✅ Idéntica | ✅ Idéntica |
|
|
430
|
-
| **Autenticación** | ❌ No requiere | ✅
|
|
777
|
+
| **Autenticación** | ❌ No requiere | ✅ Genera tokens JWT con TOTP (automático) |
|
|
778
|
+
| **Configuración** | Opciones en constructor | Variables de entorno |
|
|
779
|
+
| **Tiempos de expiración** | ❌ No aplica | ✅ Configurables mediante variables de entorno |
|
|
431
780
|
| **Ideal para** | Desarrollo, mismo servidor | Producción, múltiples apps |
|
|
432
|
-
|
|
781
|
+
|
|
782
|
+
---
|
|
433
783
|
|
|
434
784
|
## 💡 Recomendaciones
|
|
435
785
|
|
|
436
786
|
- **Desarrollo**: Usa `StorageCore` con `rootPath: './uploads'`
|
|
437
|
-
- **Producción**: Usa `StorageClient` con variables de entorno
|
|
787
|
+
- **Producción**: Usa `StorageClient` con variables de entorno y autenticación OTP
|
|
438
788
|
- **Cambio fácil**: Ambos tienen la misma API, solo cambia la inicialización
|
|
789
|
+
- **Seguridad**: El cliente renueva tokens automáticamente según los tiempos configurados
|
|
790
|
+
- **Configuración**: Usa `createClientFromConfig()` para leer automáticamente las variables de entorno
|
|
439
791
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
interface ListFilesOptions {
|
|
444
|
-
limit?: number;
|
|
445
|
-
offset?: number;
|
|
446
|
-
prefix?: string;
|
|
447
|
-
}
|
|
448
|
-
```
|
|
792
|
+
---
|
|
449
793
|
|
|
450
794
|
## Errores
|
|
451
795
|
|
|
@@ -459,6 +803,7 @@ El paquete lanza errores personalizados que extienden `StorageError`:
|
|
|
459
803
|
- `DeleteError`: Error al eliminar archivo
|
|
460
804
|
- `MetadataError`: Error al obtener metadata
|
|
461
805
|
- `ListError`: Error al listar archivos
|
|
806
|
+
- `StorageError`: Error genérico
|
|
462
807
|
|
|
463
808
|
Todos los errores tienen una propiedad `code` para identificación:
|
|
464
809
|
|
|
@@ -474,10 +819,49 @@ try {
|
|
|
474
819
|
|
|
475
820
|
## Seguridad
|
|
476
821
|
|
|
822
|
+
- **Autenticación JWT con OTP**: Sistema seguro de autenticación de dos factores
|
|
823
|
+
- **Renovación automática de tokens**: Configurable por cliente mediante variables de entorno
|
|
824
|
+
- **Validación en cadena**: Cada refresh token valida el anterior
|
|
477
825
|
- **Sanitización de rutas**: Protección contra path traversal (`..`)
|
|
478
826
|
- **Validación de caracteres**: Solo permite caracteres seguros en rutas
|
|
479
827
|
- **Validación de MIME types**: Verifica tipos de archivo permitidos
|
|
480
828
|
- **Límites de tamaño**: Previene archivos demasiado grandes
|
|
829
|
+
- **Permisos granulares**: Cada cliente tiene permisos específicos (read, write, delete)
|
|
830
|
+
- **Paths permitidos**: Cada cliente solo puede acceder a paths configurados
|
|
831
|
+
|
|
832
|
+
## Ejemplo Completo con Variables de Entorno
|
|
833
|
+
|
|
834
|
+
1. **Crear archivo `.env`:**
|
|
835
|
+
```bash
|
|
836
|
+
STORAGE_CLIENT_ID=mi-app-prod
|
|
837
|
+
STORAGE_CLIENT_NAME=Mi Aplicacion
|
|
838
|
+
STORAGE_BASE_URL=http://localhost:3000
|
|
839
|
+
STORAGE_PERMISSIONS=["read","write","delete"]
|
|
840
|
+
STORAGE_ALLOWED_PATHS=["*"]
|
|
841
|
+
STORAGE_CLIENT_SECRET=tu-clave-secreta-compartida-minimo-32-caracteres
|
|
842
|
+
STORAGE_TOTP_SECRET=tu-semilla-totp-compartida-minimo-16-caracteres
|
|
843
|
+
STORAGE_TOKEN_EXPIRES_IN=5m
|
|
844
|
+
STORAGE_TOTP_PERIOD=30
|
|
845
|
+
STORAGE_RATE_LIMIT_REQUESTS=1000
|
|
846
|
+
STORAGE_RATE_LIMIT_WINDOW=1h
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
2. **Usar el cliente:**
|
|
850
|
+
```typescript
|
|
851
|
+
import 'dotenv/config'; // Cargar variables de entorno
|
|
852
|
+
import { createClientFromConfig } from 's3-client-dtb';
|
|
853
|
+
import { readFileSync } from 'fs';
|
|
854
|
+
|
|
855
|
+
const client = createClientFromConfig(); // Lee variables de entorno
|
|
856
|
+
|
|
857
|
+
// El cliente genera tokens automáticamente
|
|
858
|
+
// Opcional: Verificar TOTP
|
|
859
|
+
await client.verifyTotp();
|
|
860
|
+
|
|
861
|
+
// Usar normalmente
|
|
862
|
+
const buffer = readFileSync('./imagen.jpg');
|
|
863
|
+
const path = await client.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
|
|
864
|
+
```
|
|
481
865
|
|
|
482
866
|
## Licencia
|
|
483
867
|
|