s3-client-dtb
v1.0.0
Published
Paquete de almacenamiento de archivos local para Node.js y NestJS
Maintainers
Readme
s3-client-dtb
Paquete de almacenamiento de archivos para Node.js y NestJS.
🎯 Dos Modos de Uso
El paquete ofrece dos modos que puedes elegir según tus necesidades:
📁 Modo Local (StorageCore)
Almacena archivos directamente en el sistema de archivos de tu servidor. Ideal para:
- Desarrollo local
- Aplicaciones que corren en el mismo servidor
- Cuando no necesitas un backend separado
🌐 Modo Cliente (StorageClient)
Se conecta a un backend remoto mediante HTTP. Ideal para:
- Producción con backend separado
- Múltiples aplicaciones compartiendo almacenamiento
- Escalabilidad y distribución
Características
- ✅ Almacenamiento local de archivos (StorageCore)
- ✅ Cliente HTTP para backend remoto (StorageClient)
- ✅ Autenticación con JWT y TOTP (Time-based OTP)
- ✅ Generación automática de tokens con CLIENT_SECRET + TOTP
- ✅ Configuración mediante variables de entorno
- ✅ Tiempos de expiración configurables por cliente
- ✅ Validación de tipos MIME
- ✅ Límites de tamaño de archivo
- ✅ Sanitización de rutas (protección contra path traversal)
- ✅ Metadata de archivos
- ✅ Listado y paginación
- ✅ Compatible con Node.js puro y NestJS
- ✅ TypeScript con tipos completos
Instalación
npm install s3-client-dtbPara uso con NestJS, también necesitas:
npm install @nestjs/common🚀 Inicio Rápido
Elegir el Modo Correcto
¿Necesitas almacenar archivos localmente? → Usa StorageCore o modo offline
¿Necesitas conectarte a un backend remoto? → Usa StorageClient o modo online
Ambos tienen la misma API, solo cambia la inicialización.
🔄 Modo Offline/Online (Recomendado para desarrollo)
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.
¿Por qué usar esto?
- En desarrollo: No necesitas tener un servidor de almacenamiento corriendo
- En producción: Usa el servidor remoto sin cambiar código
- Mismo código: La API es idéntica, solo cambian las variables de entorno
Configuración Rápida
Para desarrollo (offline):
# .env
STORAGE_MODE=offline
STORAGE_ROOT_PATH=./uploads
STORAGE_MAX_FILE_SIZE=50mb # Opcional
STORAGE_ALLOWED_MIME_TYPES=image/*,video/* # OpcionalPara producción (online):
# .env
STORAGE_MODE=online
STORAGE_BASE_URL=https://storage.example.com
STORAGE_CLIENT_ID=mi-app
STORAGE_CLIENT_NAME=Mi Aplicacion
STORAGE_PERMISSIONS=read,write,delete
STORAGE_ALLOWED_PATHS=*
STORAGE_CLIENT_SECRET=tu-clave-secreta...
STORAGE_TOTP_SECRET=tu-semilla-totp...Uso en Código
import 'dotenv/config';
import { createClientFromConfig, isOfflineClient } from 's3-client-dtb';
// ✅ Detecta automáticamente el modo según STORAGE_MODE
const client = createClientFromConfig();
// La API es idéntica en ambos modos
const path = await client.uploadFile('images', buffer, 'image/png');
const stream = await client.downloadFile(path);
const exists = await client.fileExists(path);
await client.deleteFile(path);
// Si necesitas saber en qué modo estás:
if (isOfflineClient(client)) {
console.log('Modo offline - archivos guardados localmente');
} else {
console.log('Modo online - usando servidor remoto');
}Funciones Auxiliares
import {
createClientFromConfig, // Detecta modo automáticamente
createOfflineClient, // Forzar modo offline
createOnlineClient, // Forzar modo online (deprecated, usa createClientFromConfig)
getStorageMode, // Obtener el modo actual
isOfflineClient, // Verificar si es cliente offline
isOnlineClient, // Verificar si es cliente online
} from 's3-client-dtb';
// Obtener el modo configurado
const mode = getStorageMode(); // 'online' | 'offline'📁 Modo Local (StorageCore)
Configuración
import { StorageCore } from 's3-client-dtb';
// ✅ Configuración simple - Solo necesitas la ruta
const storage = new StorageCore({
rootPath: './uploads', // Donde se guardarán los archivos
maxFileSize: '50mb', // Opcional: límite de tamaño
allowedMimeTypes: ['image/*', 'video/*'] // Opcional: tipos permitidos
});Uso Completo
import { StorageCore } from 's3-client-dtb';
import { readFileSync } from 'fs';
const storage = new StorageCore({
rootPath: './uploads',
maxFileSize: '50mb',
allowedMimeTypes: ['image/*', 'video/*']
});
// Todas las operaciones son iguales
const buffer = readFileSync('./mi-imagen.jpg');
const path = await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
const stream = await storage.downloadFile(path);
const metadata = await storage.getFileMetadata(path);
const { files, total } = await storage.listFiles('images');
await storage.deleteFile(path);
const exists = await storage.fileExists(path);🌐 Modo Cliente (StorageClient)
Configuración con Variables de Entorno
El cliente se configura mediante variables de entorno. Crea un archivo .env basado en .env.example:
# Modo de almacenamiento (online para servidor remoto)
STORAGE_MODE=online
# Variables requeridas para modo online
STORAGE_CLIENT_ID=mi-cliente-123
STORAGE_CLIENT_NAME=Mi Aplicacion
STORAGE_BASE_URL=http://localhost:3000
STORAGE_PERMISSIONS=["read","write","delete"]
STORAGE_ALLOWED_PATHS=["*"]
STORAGE_CLIENT_SECRET=tu-clave-secreta-compartida-minimo-32-caracteres
STORAGE_TOTP_SECRET=tu-semilla-totp-compartida-minimo-16-caracteres
# Variables opcionales
STORAGE_TOKEN_EXPIRES_IN=5m
STORAGE_TOTP_PERIOD=30
STORAGE_RATE_LIMIT_REQUESTS=1000
STORAGE_RATE_LIMIT_WINDOW=1hIMPORTANTE:
- Los valores de
STORAGE_CLIENT_SECRETySTORAGE_TOTP_SECRETdeben ser los mismos que están en las variables de entorno del servidor (CLIENT_SECRETyTOTP_SECRET). - Si el servidor usa
TOTP_PERIODdistinto de 30, configuraSTORAGE_TOTP_PERIODcon el mismo valor (15–300 segundos). - Para
STORAGE_PERMISSIONSySTORAGE_ALLOWED_PATHSpuedes usar JSON array (["read","write","delete"]) o valores separados por comas (read,write,delete).
🔗 Conectando con el Backend
Para que el cliente se conecte correctamente al backend, necesitas asegurarte de que ciertos valores coincidan exactamente entre ambos.
Checklist de Configuración
Antes de usar el cliente, verifica que:
- [ ]
STORAGE_CLIENT_SECRET(cliente) =CLIENT_SECRET(servidor) - [ ]
STORAGE_TOTP_SECRET(cliente) =TOTP_SECRET(servidor) - [ ]
STORAGE_TOTP_PERIOD(cliente) =TOTP_PERIOD(servidor) - Si el servidor usa un valor distinto de 30 - [ ]
STORAGE_BASE_URLapunta a la URL correcta del servidor (ej:http://localhost:3000)
Ejemplo de Configuración Completa
Backend (s3-package/.env):
CLIENT_SECRET=mi-clave-secreta-compartida-minimo-32-caracteres-1234567890
TOTP_SECRET=mi-semilla-totp-compartida-minimo-16-caracteres
TOTP_PERIOD=30
PORT=3000Cliente (tu-app/.env):
STORAGE_CLIENT_ID=mi-cliente-123
STORAGE_CLIENT_NAME=Mi Aplicacion
STORAGE_BASE_URL=http://localhost:3000
STORAGE_PERMISSIONS=["read","write","delete"]
STORAGE_ALLOWED_PATHS=["*"]
STORAGE_CLIENT_SECRET=mi-clave-secreta-compartida-minimo-32-caracteres-1234567890
STORAGE_TOTP_SECRET=mi-semilla-totp-compartida-minimo-16-caracteres
STORAGE_TOTP_PERIOD=30⚠️ Importante: Los valores de CLIENT_SECRET y TOTP_SECRET deben ser exactamente iguales en ambos archivos .env.
Verificar la Conexión
Una vez configurado, puedes verificar que la conexión funciona correctamente:
import 'dotenv/config';
import { createClientFromConfig } from 's3-client-dtb';
const client = createClientFromConfig();
// Verificar que puedes generar códigos TOTP correctos
try {
await client.verifyTotp();
console.log('✅ Conexión con el backend verificada correctamente');
} catch (error) {
console.error('❌ Error al conectar con el backend:', error);
// Verifica que los secrets coincidan y que el servidor esté corriendo
}Solución de Problemas de Conexión
Error: "UNAUTHORIZED" o "TOTP_VERIFY_ERROR"
- Verifica que
STORAGE_CLIENT_SECRETySTORAGE_TOTP_SECRETcoincidan exactamente con los del servidor - Verifica que
STORAGE_TOTP_PERIODcoincida conTOTP_PERIODdel servidor - Asegúrate de que el servidor esté corriendo y accesible en
STORAGE_BASE_URL
Error: "Timeout al conectar con el servidor"
- Verifica que
STORAGE_BASE_URLsea correcta y el servidor esté corriendo - Verifica la conectividad de red
- Aumenta el
timeouten la configuración manual si es necesario
Error: "FORBIDDEN"
- Verifica que los
STORAGE_PERMISSIONSincluyan la operación que intentas realizar - Verifica que el
STORAGE_ALLOWED_PATHSpermita la ruta que intentas usar
Cargar Variables de Entorno
Si usas Node.js, puedes usar dotenv para cargar el archivo .env:
npm install dotenvimport 'dotenv/config';
import { createClientFromConfig } from 's3-client-dtb';
const client = createClientFromConfig();O cargar manualmente:
import { config } from 'dotenv';
config(); // Carga .env desde el directorio actual
import { createClientFromConfig } from 's3-client-dtb';
const client = createClientFromConfig();Crear Cliente desde Variables de Entorno
import { createClientFromConfig } from 's3-client-dtb';
// Lee las variables de entorno automáticamente
const client = createClientFromConfig();Configuración Manual
import { StorageClient } from 's3-client-dtb';
import type { ClientConfig } from 's3-client-dtb';
const clientConfig: ClientConfig = {
clientId: 'mi-cliente-123',
name: 'Mi Aplicacion',
permissions: ['read', 'write', 'delete'],
allowedPaths: ['*'],
rateLimit: {
requests: 1000,
window: '1h'
},
tokenExpiresIn: '5m'
};
const client = new StorageClient({
baseUrl: 'https://api.midominio.com',
clientConfig,
clientSecret: 'tu-clave-secreta-compartida-minimo-32-caracteres',
totpSecret: 'tu-semilla-totp-compartida-minimo-16-caracteres',
totpPeriodSeconds: 30, // opcional; debe coincidir con TOTP_PERIOD del servidor
});Autenticación Automática
El cliente genera tokens automáticamente usando TOTP:
import { createClientFromConfig } from 's3-client-dtb';
const client = createClientFromConfig();
// El cliente genera tokens automáticamente usando CLIENT_SECRET + TOTP
// No necesitas llamar ningún método de autenticación manualmente
// Opcional: Verificar que puedes generar códigos TOTP correctos
await client.verifyTotp();
// Ahora puedes usar el cliente normalmente
// Los tokens se generan y renuevan automáticamenteVariables de Entorno
Requeridas
STORAGE_CLIENT_ID(string): Identificador único del clienteSTORAGE_CLIENT_NAME(string): Nombre descriptivo de la aplicaciónSTORAGE_BASE_URL(string): URL del backend (ej:http://localhost:3000)STORAGE_PERMISSIONS(string): Permisos del cliente. Puede ser JSON array (["read","write","delete"]) o valores separados por comas (read,write,delete)STORAGE_ALLOWED_PATHS(string): Paths permitidos. Puede ser JSON array (["*"]o["images/*","documents/*"]) o valores separados por comas (*oimages/*,documents/*)STORAGE_CLIENT_SECRET(string): Clave secreta compartida con el servidor (mínimo 32 caracteres)STORAGE_TOTP_SECRET(string): Semilla TOTP compartida con el servidor (mínimo 16 caracteres)
Opcionales
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)STORAGE_TOTP_PERIOD(number): Período TOTP en segundos (15–300, default 30). Debe coincidir conTOTP_PERIODdel servidor.STORAGE_RATE_LIMIT_REQUESTS(number): Número de peticiones permitidas (solo se usa si también definesSTORAGE_RATE_LIMIT_WINDOW)STORAGE_RATE_LIMIT_WINDOW(string): Ventana de tiempo para el rate limit (ej:"1h","30m"). Solo se usa si también definesSTORAGE_RATE_LIMIT_REQUESTS
Tabla de Referencia Rápida
| Variable | Requerida | Tipo | Default | Descripción |
|----------|-----------|------|---------|-------------|
| STORAGE_CLIENT_ID | ✅ | string | - | Identificador único del cliente |
| STORAGE_CLIENT_NAME | ✅ | string | - | Nombre descriptivo de la aplicación |
| STORAGE_BASE_URL | ✅ | string | - | URL del backend (ej: http://localhost:3000) |
| STORAGE_PERMISSIONS | ✅ | string | - | Permisos: JSON array o comma-separated (["read","write","delete"] o read,write,delete) |
| STORAGE_ALLOWED_PATHS | ✅ | string | - | Paths permitidos: JSON array o comma-separated (["*"] o *) |
| STORAGE_CLIENT_SECRET | ✅ | string | - | Clave secreta compartida (mínimo 32 caracteres). Debe coincidir con CLIENT_SECRET del servidor |
| STORAGE_TOTP_SECRET | ✅ | string | - | Semilla TOTP compartida (mínimo 16 caracteres). Debe coincidir con TOTP_SECRET del servidor |
| STORAGE_TOKEN_EXPIRES_IN | ❌ | string | 5m | Duración del token JWT (30s, 5m, 1h, 7d) |
| STORAGE_TOTP_PERIOD | ❌ | number | 30 | Período TOTP en segundos (15-300). Debe coincidir con TOTP_PERIOD del servidor |
| STORAGE_RATE_LIMIT_REQUESTS | ❌ | number | - | Número de peticiones permitidas (requiere STORAGE_RATE_LIMIT_WINDOW) |
| STORAGE_RATE_LIMIT_WINDOW | ❌ | string | - | Ventana de tiempo para rate limit (1h, 30m, 7d) |
Ejemplos de baseUrl
// Con dominio
new StorageClient({
baseUrl: 'https://api.midominio.com',
clientConfig
});
// Con IP directa
new StorageClient({
baseUrl: 'http://192.168.1.100:3000',
clientConfig
});
// Con puerto personalizado
new StorageClient({
baseUrl: 'http://localhost:8080',
clientConfig
});Uso Completo
import 'dotenv/config'; // Cargar variables de entorno
import { createClientFromConfig } from 's3-client-dtb';
import {
FileNotFoundError,
UploadError,
StorageError
} from 's3-client-dtb';
import { readFileSync } from 'fs';
// Inicializar cliente desde variables de entorno
const client = createClientFromConfig();
// Opcional: Verificar que la configuración TOTP es correcta
try {
await client.verifyTotp();
console.log('✅ Cliente configurado correctamente');
} catch (error) {
console.error('❌ Error en configuración:', error);
process.exit(1);
}
// Ejemplo: Subir un archivo
try {
const buffer = readFileSync('./mi-imagen.jpg');
const path = await client.uploadFile(
'images/photo.jpg', // Ruta donde se guardará
buffer, // Buffer del archivo
'image/jpeg' // Tipo MIME
);
console.log('✅ Archivo subido en:', path);
} catch (error) {
if (error instanceof UploadError) {
console.error('❌ Error al subir:', error.message);
} else {
console.error('❌ Error inesperado:', error);
}
}
// Ejemplo: Descargar un archivo
try {
const stream = await client.downloadFile('images/photo.jpg');
// Convertir stream a buffer si es necesario
const chunks: Buffer[] = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
const downloadedBuffer = Buffer.concat(chunks);
console.log('✅ Archivo descargado:', downloadedBuffer.length, 'bytes');
} catch (error) {
if (error instanceof FileNotFoundError) {
console.error('❌ Archivo no encontrado');
} else {
console.error('❌ Error al descargar:', error);
}
}
// Ejemplo: Obtener metadata
try {
const metadata = await client.getFileMetadata('images/photo.jpg');
console.log('Metadata:', {
nombre: metadata.name,
tamaño: metadata.size,
tipo: metadata.mimeType,
creado: metadata.createdAt
});
} catch (error) {
console.error('❌ Error al obtener metadata:', error);
}
// Ejemplo: Listar archivos
try {
const { files, total } = await client.listFiles('images', {
limit: 10, // Máximo 10 archivos
offset: 0, // Desde el inicio
prefix: 'photo' // Solo archivos que empiecen con "photo"
});
console.log(`✅ Encontrados ${files.length} de ${total} archivos`);
files.forEach(file => {
console.log(` - ${file.name} (${file.size} bytes)`);
});
} catch (error) {
console.error('❌ Error al listar archivos:', error);
}
// Ejemplo: Verificar existencia
const exists = await client.fileExists('images/photo.jpg');
console.log('Archivo existe:', exists);
// Ejemplo: Eliminar archivo
try {
await client.deleteFile('images/photo.jpg');
console.log('✅ Archivo eliminado');
} catch (error) {
if (error instanceof FileNotFoundError) {
console.error('❌ Archivo no encontrado para eliminar');
} else {
console.error('❌ Error al eliminar:', error);
}
}Gestión de Tokens
El cliente genera tokens automáticamente usando TOTP:
- Generación automática: El cliente genera tokens JWT usando
CLIENT_SECRET + TOTP_CODE - TOTP dinámico: El código TOTP cambia cada 30 segundos, proporcionando seguridad adicional
- Renovación automática: Los tokens se regeneran automáticamente cuando expiran o cuando cambia el código TOTP
- Caché inteligente: Los tokens se cachean y se regeneran solo cuando es necesario
- Errores 401: El cliente regenera el token automáticamente si recibe un 401
// El cliente siempre está listo para usar, genera tokens automáticamente
// No necesitas métodos de autenticación manual🔄 Cambiar Entre Modos
Como ambos tienen la misma API, cambiar de modo es súper simple:
Opción 1: Variable de Entorno
import { StorageCore, createClientFromConfig } from 's3-client-dtb';
// Usar variable de entorno para decidir el modo
const useRemote = process.env.STORAGE_MODE === 'remote';
const storage = useRemote
? createClientFromConfig() // Lee variables de entorno automáticamente
: new StorageCore({
rootPath: process.env.STORAGE_PATH || './uploads'
});
// Si es remoto, el cliente genera tokens automáticamente
// Opcional: Verificar TOTP
if (useRemote && storage instanceof StorageClient) {
await storage.verifyTotp();
}
// El resto del código es idéntico
await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');Opción 2: Factory Function
import { StorageCore, createClientFromConfig } from 's3-client-dtb';
async function createStorage() {
if (process.env.STORAGE_URL) {
// Modo remoto - usa variables de entorno
const client = createClientFromConfig();
// El cliente genera tokens automáticamente
// Opcional: Verificar TOTP
await client.verifyTotp();
return client;
}
// Modo local (por defecto)
return new StorageCore({
rootPath: process.env.STORAGE_PATH || './uploads'
});
}
const storage = await createStorage();
// Usar normalmente
await storage.uploadFile('images/photo.jpg', buffer, 'image/jpeg');Uso desde NestJS
Configuración básica
import { Module } from '@nestjs/common';
import { StorageModule } from 's3-client-dtb/nestjs';
@Module({
imports: [
StorageModule.forRoot({
rootPath: './uploads',
maxFileSize: '50mb',
allowedMimeTypes: ['image/*', 'video/*']
})
]
})
export class AppModule {}Configuración asíncrona
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { StorageModule } from 's3-client-dtb/nestjs';
@Module({
imports: [
ConfigModule.forRoot(),
StorageModule.forRootAsync({
useFactory: (config: ConfigService) => ({
rootPath: config.get('STORAGE_ROOT_PATH', './uploads'),
maxFileSize: config.get('STORAGE_MAX_SIZE', '50mb'),
allowedMimeTypes: config.get('STORAGE_ALLOWED_TYPES', ['image/*'])
}),
inject: [ConfigService]
})
]
})
export class AppModule {}Usar en un servicio
import { Injectable } from '@nestjs/common';
import { StorageService } from 's3-client-dtb/nestjs';
@Injectable()
export class FilesService {
constructor(private readonly storage: StorageService) {}
async uploadFile(filePath: string, buffer: Buffer, mimeType: string) {
return this.storage.uploadFile(filePath, buffer, mimeType);
}
async downloadFile(path: string) {
return this.storage.downloadFile(path);
}
}📖 API Completa
⚡ Métodos Comunes (Ambos Modos)
Ambos StorageCore y StorageClient tienen exactamente la misma API:
// Subir archivo
uploadFile(path: string, buffer: Buffer, mimeType: string, prefix?: string): Promise<string>
// Descargar archivo (retorna stream)
downloadFile(path: string): Promise<Readable>
// Eliminar archivo
deleteFile(path: string): Promise<void>
// Obtener metadata
getFileMetadata(path: string): Promise<FileMetadata>
// Listar archivos
listFiles(directoryPath?: string, options?: ListFilesOptions): Promise<{files: FileMetadata[], total: number}>
// Verificar existencia
fileExists(path: string): Promise<boolean>🔧 Configuración
StorageCore (Modo Local)
new StorageCore(options: StorageCoreOptions)
interface StorageCoreOptions {
rootPath: string; // Ruta donde se guardan archivos
maxFileSize?: string; // Opcional: "50mb", "1gb", etc.
allowedMimeTypes?: string[]; // Opcional: ["image/*", "video/*"]
}StorageClient (Modo Remoto)
// Opción 1: Desde variables de entorno (recomendado)
createClientFromConfig(): StorageClient
// Opción 2: Manual
new StorageClient(options: StorageClientOptions)
interface StorageClientOptions {
baseUrl: string; // URL del backend
clientConfig: ClientConfig; // Configuración del cliente
clientSecret: string; // Clave secreta compartida (mínimo 32 caracteres). Debe coincidir con CLIENT_SECRET del servidor
totpSecret: string; // Semilla TOTP compartida (mínimo 16 caracteres). Debe coincidir con TOTP_SECRET del servidor
totpPeriodSeconds?: number; // Opcional: Período TOTP en segundos (15-300, default: 30). Debe coincidir con TOTP_PERIOD del servidor
timeout?: number; // Opcional: timeout en ms (default: 30000)
}
interface ClientConfig {
clientId: string;
name: string;
permissions: ('read' | 'write' | 'delete')[];
allowedPaths: string[];
rateLimit?: {
requests: number;
window: string;
};
tokenExpiresIn?: string; // Opcional: "30m", "1h", "5m", "7d"
}Nota:
- El cliente automáticamente agrega el prefijo
/apifilesa las URLs - Genera tokens automáticamente usando
CLIENT_SECRET + TOTP(no requiere autenticación manual) - Regenera tokens automáticamente cuando expiran o cuando cambia el código TOTP
Tipos
FileMetadata
interface FileMetadata {
name: string;
path: string;
size: number;
mimeType: string;
createdAt: Date;
modifiedAt: Date;
checksum?: string;
}ListFilesOptions
interface ListFilesOptions {
limit?: number;
offset?: number;
prefix?: string;
}📋 Comparación Rápida
| Característica | StorageCore (Local) | StorageClient (Remoto) |
|---------------|---------------------|------------------------|
| Inicialización | new StorageCore({ rootPath }) | createClientFromConfig() o new StorageClient({ baseUrl, clientConfig }) |
| Almacenamiento | Sistema de archivos local | Backend remoto vía HTTP |
| API | ✅ Idéntica | ✅ Idéntica |
| Autenticación | ❌ No requiere | ✅ Genera tokens JWT con TOTP (automático) |
| Configuración | Opciones en constructor | Variables de entorno |
| Tiempos de expiración | ❌ No aplica | ✅ Configurables mediante variables de entorno |
| Ideal para | Desarrollo, mismo servidor | Producción, múltiples apps |
💡 Recomendaciones
- Desarrollo: Usa
StorageCoreconrootPath: './uploads' - Producción: Usa
StorageClientcon variables de entorno y autenticación OTP - Cambio fácil: Ambos tienen la misma API, solo cambia la inicialización
- Seguridad: El cliente renueva tokens automáticamente según los tiempos configurados
- Configuración: Usa
createClientFromConfig()para leer automáticamente las variables de entorno
Errores
El paquete lanza errores personalizados que extienden StorageError:
FileNotFoundError: El archivo no existeInvalidMimeTypeError: Tipo MIME no permitidoFileTooLargeError: El archivo excede el tamaño máximoUploadError: Error al subir archivoDownloadError: Error al descargar archivoDeleteError: Error al eliminar archivoMetadataError: Error al obtener metadataListError: Error al listar archivosStorageError: Error genérico
Todos los errores tienen una propiedad code para identificación:
try {
await storage.uploadFile('path', buffer, 'image/jpeg');
} catch (error) {
if (error instanceof FileNotFoundError) {
console.error('Código:', error.code); // 'FILE_NOT_FOUND'
}
}Seguridad
- Autenticación JWT con OTP: Sistema seguro de autenticación de dos factores
- Renovación automática de tokens: Configurable por cliente mediante variables de entorno
- Validación en cadena: Cada refresh token valida el anterior
- Sanitización de rutas: Protección contra path traversal (
..) - Validación de caracteres: Solo permite caracteres seguros en rutas
- Validación de MIME types: Verifica tipos de archivo permitidos
- Límites de tamaño: Previene archivos demasiado grandes
- Permisos granulares: Cada cliente tiene permisos específicos (read, write, delete)
- Paths permitidos: Cada cliente solo puede acceder a paths configurados
Ejemplo Completo con Variables de Entorno
Crear archivo
.env:STORAGE_CLIENT_ID=mi-app-prod STORAGE_CLIENT_NAME=Mi Aplicacion STORAGE_BASE_URL=http://localhost:3000 STORAGE_PERMISSIONS=["read","write","delete"] STORAGE_ALLOWED_PATHS=["*"] STORAGE_CLIENT_SECRET=tu-clave-secreta-compartida-minimo-32-caracteres STORAGE_TOTP_SECRET=tu-semilla-totp-compartida-minimo-16-caracteres STORAGE_TOKEN_EXPIRES_IN=5m STORAGE_TOTP_PERIOD=30 STORAGE_RATE_LIMIT_REQUESTS=1000 STORAGE_RATE_LIMIT_WINDOW=1hUsar el cliente:
import 'dotenv/config'; // Cargar variables de entorno import { createClientFromConfig } from 's3-client-dtb'; import { readFileSync } from 'fs'; const client = createClientFromConfig(); // Lee variables de entorno // El cliente genera tokens automáticamente // Opcional: Verificar TOTP await client.verifyTotp(); // Usar normalmente const buffer = readFileSync('./imagen.jpg'); const path = await client.uploadFile('images/photo.jpg', buffer, 'image/jpeg');
Licencia
MIT
