Files
perfil/nuxt4/server/api/share-target.post.ts
josedario87 5bb5e5092e
Some checks failed
build-and-deploy / build-and-deploy (push) Has been cancelled
Implementar Web Share Target API para compartir fotos con la PWA
- Agregar share_target al manifest de la PWA
- Crear endpoint /api/share-target para recibir archivos compartidos
- Guardar archivos temporalmente en /public/temp-shared
- Modificar UserProfileForm para aceptar imágenes externas
- Detectar automáticamente imágenes compartidas y procesarlas
- Crear endpoint /api/share-target/cleanup para limpiar temporales
- Mostrar toast informativo al recibir imagen compartida
- Redirigir automáticamente al formulario de perfil
- Soportar compartir desde galería, otras apps, etc.
2025-10-17 18:29:00 -06:00

76 lines
2.4 KiB
TypeScript

/**
* API endpoint para Web Share Target
* POST /api/share-target
*
* Recibe imágenes compartidas desde otras apps y las almacena temporalmente
*/
import { promises as fs } from 'fs'
import { join } from 'path'
import { createHash, randomBytes } from 'crypto'
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/jpg']
const TEMP_DIR = 'public/temp-shared'
export default defineEventHandler(async (event) => {
try {
// Leer el body como multipart/form-data
const formData = await readMultipartFormData(event)
if (!formData || formData.length === 0) {
// No hay archivo, redirigir a inicio
return sendRedirect(event, '/', 302)
}
// Buscar el campo 'media' (nombre definido en manifest)
const mediaFile = formData.find(field => field.name === 'media')
if (!mediaFile) {
// No hay imagen, redirigir a inicio
return sendRedirect(event, '/', 302)
}
// Validar tipo de archivo
const contentType = mediaFile.type || ''
if (!ALLOWED_TYPES.includes(contentType)) {
console.error('Tipo de archivo no permitido:', contentType)
return sendRedirect(event, '/', 302)
}
// Validar tamaño
if (mediaFile.data.length > MAX_FILE_SIZE) {
console.error('Archivo muy grande:', mediaFile.data.length)
return sendRedirect(event, '/', 302)
}
// Crear directorio temporal si no existe
const tempPath = join(process.cwd(), TEMP_DIR)
try {
await fs.access(tempPath)
} catch {
await fs.mkdir(tempPath, { recursive: true })
}
// Generar token único para este archivo temporal
const token = randomBytes(16).toString('hex')
const timestamp = Date.now()
const extension = contentType.split('/')[1]
const filename = `shared-${token}-${timestamp}.${extension}`
const filepath = join(tempPath, filename)
// Guardar archivo temporal
await fs.writeFile(filepath, mediaFile.data)
// URL pública del archivo temporal
const tempUrl = `/temp-shared/${filename}`
console.log(`✅ Imagen compartida guardada temporalmente: ${tempUrl}`)
// Redirigir a la página principal con el token
return sendRedirect(event, `/?shared=${token}&file=${encodeURIComponent(filename)}`, 302)
} catch (error: any) {
console.error('Error en share-target:', error)
return sendRedirect(event, '/', 302)
}
})