Some checks failed
build-and-deploy / build-and-deploy (push) Has been cancelled
- 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.
76 lines
2.4 KiB
TypeScript
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)
|
|
}
|
|
})
|