npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

s3-client-dtb

v1.0.0

Published

Paquete de almacenamiento de archivos local para Node.js y NestJS

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-dtb

Para 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/*  # Opcional

Para 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=1h

IMPORTANTE:

  • 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).
  • Si el servidor usa TOTP_PERIOD distinto de 30, configura STORAGE_TOTP_PERIOD con el mismo valor (15–300 segundos).
  • Para STORAGE_PERMISSIONS y STORAGE_ALLOWED_PATHS puedes 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_URL apunta 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=3000

Cliente (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_SECRET y STORAGE_TOTP_SECRET coincidan exactamente con los del servidor
  • Verifica que STORAGE_TOTP_PERIOD coincida con TOTP_PERIOD del 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_URL sea correcta y el servidor esté corriendo
  • Verifica la conectividad de red
  • Aumenta el timeout en la configuración manual si es necesario

Error: "FORBIDDEN"

  • Verifica que los STORAGE_PERMISSIONS incluyan la operación que intentas realizar
  • Verifica que el STORAGE_ALLOWED_PATHS permita la ruta que intentas usar

Cargar Variables de Entorno

Si usas Node.js, puedes usar dotenv para cargar el archivo .env:

npm install dotenv
import '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áticamente

Variables de Entorno

Requeridas

  • 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 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 (* o images/*,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 con TOTP_PERIOD del servidor.
  • STORAGE_RATE_LIMIT_REQUESTS (number): Número de peticiones permitidas (solo se usa si también defines STORAGE_RATE_LIMIT_WINDOW)
  • 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

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 /apifiles a 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 StorageCore con rootPath: './uploads'
  • Producción: Usa StorageClient con 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 existe
  • InvalidMimeTypeError: Tipo MIME no permitido
  • FileTooLargeError: El archivo excede el tamaño máximo
  • UploadError: Error al subir archivo
  • DownloadError: Error al descargar archivo
  • DeleteError: Error al eliminar archivo
  • MetadataError: Error al obtener metadata
  • ListError: Error al listar archivos
  • StorageError: 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

  1. 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=1h
  2. Usar 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