Recursos de Lección en S3
Sección titulada «Recursos de Lección en S3»Los recursos de lección son archivos adjuntos que el administrador sube a cada lección: PDFs, presentaciones, archivos de código, hojas de referencia, etc. Son distintos de los videos de lección.
Estructura del Bucket
Sección titulada «Estructura del Bucket»Todos los archivos de la plataforma (recursos, videos y archivos de usuario) conviven en el mismo bucket S3, separados por prefijos:
{bucket}/├── lessons/│ ├── {lessonId}/│ │ ├── resources/│ │ │ └── {uuid}.{ext} ← recursos de lección (PDFs, docs, etc.)│ │ └── videos/│ │ └── {uuid}.{ext} ← videos de lección (MP4, WebM, MOV)└── {userId}/ └── {uuid}.{ext} ← archivos de usuario (avatares, uploads)La clave S3 para un recurso se genera en resourceS3Utils.ts:
// lessons/{lessonId}/resources/{randomUUID()}.{ext}function getResourceS3Key(fileName: string, lessonId: number) { const ext = path.extname(fileName).slice(1); return `lessons/${lessonId}/resources/${randomUUID()}.${ext}`;}Flujo de Subida (Admin)
Sección titulada «Flujo de Subida (Admin)»El componente ResourceUploadSection gestiona todo el proceso en tres pasos:
┌─────────────────────────────────────────────────────────────────────┐│ FLUJO DE SUBIDA ││ ││ Admin Servidor (Wasp) Proveedor S3/Azure ││ ────── ────────────── ───────────────── ││ ││ 1. Selecciona ││ archivo ──────────────▶ ││ createLessonResource ││ (valida admin, tamaño, ││ tipo; crea registro DB) ││ ││ ◀────────────── { s3UploadUrl, ││ s3UploadFields, ││ resourceId, ││ uploadMethod } ││ ││ 2. PUT / POST ││ directo ────────────────────────────▶ S3 / Azure Blob ││ al cloud (sin pasar por servidor) ││ ││ 3. UI ││ actualiza ││ lista local │└─────────────────────────────────────────────────────────────────────┘Diferencia PUT vs POST
Sección titulada «Diferencia PUT vs POST»| Proveedor | Método | Notas |
|---|---|---|
| AWS S3 | POST (multipart) | Requiere s3UploadFields adicionales en el FormData |
| Azure Blob | PUT con header x-ms-blob-type: BlockBlob | Sin campos adicionales |
El cliente detecta el método con la propiedad uploadMethod devuelta por createLessonResource.
Flujo de Descarga (Estudiantes)
Sección titulada «Flujo de Descarga (Estudiantes)»Cuando un estudiante accede a los recursos de una lección:
┌──────────────────────────────────────────────────────────────────┐│ FLUJO DE DESCARGA ││ ││ Estudiante Servidor (Wasp) S3 directo / Azure Blob ││ ────────── ────────────── ────────────────────── ││ ││ 1. Solicita ──────────────▶ ││ recurso getResourceDownloadSignedURL ││ (verifica matrícula / suscripción) ││ ││ getStorageProvider() ││ .getDownloadUrl(s3Key) ──────▶ genera URL ││ firmada (1h) ││ AWS: GetObjectCommand + getSignedUrl ││ Azure: SAS URL (permisos solo lectura) ││ ││ ◀─────────────────────────────── URL firmada ││ ││ 2. Descarga ──────────────────────────────▶ archivo ││ directo │└──────────────────────────────────────────────────────────────────┘Tipos y Límites de Archivo
Sección titulada «Tipos y Límites de Archivo»| Tipo | Límite | Formatos permitidos |
|---|---|---|
| Recursos de lección | 50 MB | PDF, DOCX, DOC, PPTX, PPT, ZIP, RAR, 7Z, JSON, texto/código, imágenes (JPEG, PNG, GIF), MP4, MOV |
| Videos de lección | 5 GB | MP4, WebM, MOV |
| Archivos de usuario | 5 MB | Imágenes (JPEG, PNG, GIF) |
Los tipos MIME válidos se definen en app/src/file-upload/validation.ts:
export const ALLOWED_FILE_TYPES = [ "image/jpeg", "image/png", "image/gif", "application/pdf", "application/vnd.ms-powerpoint", // .ppt "application/vnd.openxmlformats-officedocument.presentationml.presentation", // .pptx "application/msword", // .doc "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .docx "application/zip", "application/x-rar-compressed", "application/x-7z-compressed", "text/*", "application/json", "video/quicktime", "video/mp4",];Variables de Entorno
Sección titulada «Variables de Entorno»Las mismas variables de almacenamiento utilizadas para videos se aplican también a los recursos. Ver la guía Subir Videos → Variables de Entorno para la configuración completa.
En resumen:
# Proveedor activo (aws por defecto)STORAGE_PROVIDER=aws
# AWS S3 (requerido para STORAGE_PROVIDER=aws)AWS_S3_IAM_ACCESS_KEY=AKIA...AWS_S3_IAM_SECRET_KEY=...AWS_S3_FILES_BUCKET=nombre-del-bucketAWS_S3_REGION=us-east-1
# Azure (alternativa a AWS)# STORAGE_PROVIDER=azure# AZURE_STORAGE_ACCOUNT_NAME=talentbricksai# AZURE_STORAGE_ACCOUNT_KEY=...# AZURE_STORAGE_CONTAINER_NAME=archivosGuía para el Administrador
Sección titulada «Guía para el Administrador»Agregar un recurso a una lección
Sección titulada «Agregar un recurso a una lección»- Ir al Panel de Admin → Cursos → seleccionar el curso.
- Dentro del curso, hacer clic en la lección deseada para editarla.
- En la sección Recursos, hacer clic en “Agregar recurso”.
- Seleccionar el archivo en el explorador (máx. 50 MB, formatos válidos según la tabla anterior).
- Esperar a que aparezca el indicador de carga — el archivo se sube directamente al almacenamiento en la nube.
- El recurso aparece en la lista con el nombre del archivo como título por defecto.
Editar título y descripción de un recurso
Sección titulada «Editar título y descripción de un recurso»- En la lista de recursos, hacer clic en “Editar” junto al recurso.
- Modificar el título (visible para el estudiante) y la descripción (opcional).
- Hacer clic en “Guardar”.
El título y la descripción se actualizan solo en la base de datos — el archivo en S3 no se mueve.
Eliminar un recurso
Sección titulada «Eliminar un recurso»- Hacer clic en el ícono de papelera junto al recurso.
- Confirmar la eliminación en el diálogo.
Seguridad
Sección titulada «Seguridad»- Sin acceso público al bucket: el bucket debe configurarse como privado (sin ACLs públicas).
- URLs firmadas con expiración de 1 hora:
- AWS:
GetObjectCommand+getSignedUrl({ expiresIn: 3600 })→ URL de S3 firmada. - Azure: SAS con
BlobSASPermissions.parse("r")(solo lectura) yexpiresOn = ahora + 1h.
- AWS:
- SAS de subida con permisos mínimos (Azure): la URL de subida usa
BlobSASPermissions.parse("w")(solo escritura), distinta de la URL de descarga (solo lectura). - Límite de tamaño aplicado en AWS, validado en cliente para Azure:
- AWS: el presigned POST incluye
Conditions: [["content-length-range", 0, maxBytes]]— S3 rechaza archivos que superen el límite aunque el cliente lo intente. - Azure: la SAS no admite restricciones de tamaño. El límite de 50 MB se aplica únicamente
mediante validación en el cliente (
ResourceUploadSection) antes de iniciar la subida.
- AWS: el presigned POST incluye
- Verificación de acceso: antes de generar la URL de descarga, el servidor comprueba que el usuario tenga matrícula activa en el curso o una suscripción vigente.
- Subida directa al cloud: los archivos se suben desde el navegador del admin directamente a S3/Azure, sin pasar por el servidor de la aplicación. Esto reduce la carga del servidor y evita almacenar temporalmente archivos grandes en memoria.
Referencia: Interfaz StorageProvider
Sección titulada «Referencia: Interfaz StorageProvider»Ambos proveedores implementan la misma interfaz definida en app/src/storage/types.ts:
export interface StorageProvider { // Genera una URL firmada para subir un archivo al bucket getUploadUrl(params: { key: string; // clave S3 / blob name contentType: string; maxBytes: number; // aplicado en AWS; ignorado en Azure }): Promise<UploadUrlResult>;
// Genera una URL firmada para descargar un archivo (1 hora) getDownloadUrl(key: string): Promise<string>;
// Elimina un archivo del bucket deleteFile(key: string): Promise<void>;
// Comprueba si un archivo existe (usado en validaciones internas) checkFileExists(key: string): Promise<boolean>;}| Método | AWS | Azure |
|---|---|---|
getUploadUrl | Presigned POST (createPresignedPost) | SAS URL con permisos "w" |
getDownloadUrl | GetObjectCommand + getSignedUrl | SAS URL con permisos "r" |
deleteFile | DeleteObjectCommand | blockBlobClient.delete() |
checkFileExists | HeadObjectCommand | blockBlobClient.exists() |
Desarrollo Local
Sección titulada «Desarrollo Local»En entorno de desarrollo no es necesario configurar S3 ni Azure para trabajar con el código de la aplicación. Sin embargo, la funcionalidad de subida de recursos sí requiere credenciales de almacenamiento (no existe modo simulado para recursos, a diferencia de los videos de desarrollo que usan URLs públicas W3C).
Para probar recursos en local:
- Configura las variables de entorno de AWS o Azure en
.env.server. - Usa un bucket de desarrollo separado (p. ej.
talentbricksai-dev). - Ejecuta
wasp startnormalmente.