IMPLEMENTACION DE LOS VIDEOS EN EL CATALOGO PUBLICO
This commit is contained in:
@@ -23,6 +23,7 @@ const DATA_DIR = path.join(__dirname, 'data');
|
||||
const FRONTEND_DIR = path.join(__dirname, 'frontend');
|
||||
const IMG_DIR = path.join(__dirname, 'Imagenes');
|
||||
const PEDIDOS_DIR = path.join(__dirname, 'pedidos');
|
||||
const VIDEOS_DIR = path.join(__dirname, 'videos');
|
||||
|
||||
// ════════════════════════════════
|
||||
// MIDDLEWARES
|
||||
@@ -32,6 +33,7 @@ app.use(cookieParser(SECRET));
|
||||
app.use(express.static(FRONTEND_DIR));
|
||||
app.use('/Imagenes', express.static(IMG_DIR, { maxAge: '7d' }));
|
||||
app.use('/pedidos', express.static(PEDIDOS_DIR));
|
||||
app.use('/videos', express.static(VIDEOS_DIR));
|
||||
|
||||
// Evita el 404 de favicon.ico
|
||||
app.get('/favicon.ico', (req, res) => res.status(204).end());
|
||||
@@ -2162,6 +2164,169 @@ app.post('/api/pedidos/generar-pdf', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ════════════════════════════════
|
||||
// REDES SOCIALES — gestión de publicaciones embebidas
|
||||
// ════════════════════════════════
|
||||
const REDES_FILE = 'redes.json';
|
||||
|
||||
async function getRedes() {
|
||||
try { return await readJson(REDES_FILE); }
|
||||
catch { return []; }
|
||||
}
|
||||
|
||||
function extraerPlataforma(url) {
|
||||
if (/instagram\.com/i.test(url)) return 'instagram';
|
||||
if (/facebook\.com/i.test(url)) return 'facebook';
|
||||
return null;
|
||||
}
|
||||
|
||||
// Público: solo activas
|
||||
app.get('/api/redes', async (req, res) => {
|
||||
const todas = await getRedes();
|
||||
res.json(todas.filter(r => r.activo).sort((a, b) => a.orden - b.orden));
|
||||
});
|
||||
|
||||
// Admin: todas
|
||||
app.get('/api/redes/admin', requireAdmin, async (req, res) => {
|
||||
const todas = await getRedes();
|
||||
res.json(todas.sort((a, b) => a.orden - b.orden));
|
||||
});
|
||||
|
||||
app.post('/api/redes', requireAdmin, async (req, res) => {
|
||||
const { url } = req.body || {};
|
||||
if (!url || !url.trim()) {
|
||||
return res.status(400).json({ error: 'La URL es obligatoria' });
|
||||
}
|
||||
const plataforma = extraerPlataforma(url);
|
||||
if (!plataforma) {
|
||||
return res.status(400).json({ error: 'La URL debe ser de Instagram o Facebook' });
|
||||
}
|
||||
const todas = await getRedes();
|
||||
const nueva = {
|
||||
id: String(Date.now()),
|
||||
url: url.trim(),
|
||||
plataforma,
|
||||
activo: true,
|
||||
orden: todas.length
|
||||
};
|
||||
todas.push(nueva);
|
||||
await writeJson(REDES_FILE, todas);
|
||||
res.json(todas);
|
||||
});
|
||||
|
||||
app.delete('/api/redes/:id', requireAdmin, async (req, res) => {
|
||||
let todas = await getRedes();
|
||||
todas = todas.filter(r => r.id !== req.params.id);
|
||||
await writeJson(REDES_FILE, todas);
|
||||
res.json(todas);
|
||||
});
|
||||
|
||||
app.put('/api/redes/:id', requireAdmin, async (req, res) => {
|
||||
const todas = await getRedes();
|
||||
const idx = todas.findIndex(r => r.id === req.params.id);
|
||||
if (idx === -1) return res.status(404).json({ error: 'No encontrada' });
|
||||
const { activo } = req.body || {};
|
||||
if (typeof activo === 'boolean') todas[idx].activo = activo;
|
||||
await writeJson(REDES_FILE, todas);
|
||||
res.json(todas);
|
||||
});
|
||||
|
||||
// ════════════════════════════════
|
||||
// VIDEOS — gestión de videos subidos
|
||||
// ════════════════════════════════
|
||||
const MAX_VIDEOS = 10;
|
||||
|
||||
const videoUpload = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: VIDEOS_DIR,
|
||||
filename: (req, file, cb) => {
|
||||
const ext = path.extname(file.originalname) || '.mp4';
|
||||
cb(null, `video-${Date.now()}${ext}`);
|
||||
}
|
||||
}),
|
||||
limits: { fileSize: 200 * 1024 * 1024 } // 200MB
|
||||
});
|
||||
|
||||
app.get('/api/videos', async (req, res) => {
|
||||
try {
|
||||
await fs.mkdir(VIDEOS_DIR, { recursive: true });
|
||||
const files = await fs.readdir(VIDEOS_DIR);
|
||||
const videos = files
|
||||
.filter(f => f.startsWith('video-'))
|
||||
.sort()
|
||||
.reverse()
|
||||
.map(f => ({ filename: f, url: `/videos/${f}` }));
|
||||
res.json(videos);
|
||||
} catch (err) {
|
||||
console.error('Error listing videos:', err);
|
||||
res.status(500).json({ error: 'Error al listar videos' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/videos', requireAdmin, videoUpload.single('video'), async (req, res) => {
|
||||
try {
|
||||
await fs.mkdir(VIDEOS_DIR, { recursive: true });
|
||||
const files = await fs.readdir(VIDEOS_DIR);
|
||||
const videoFiles = files.filter(f => f.startsWith('video-'));
|
||||
if (videoFiles.length > MAX_VIDEOS) {
|
||||
// Eliminar el que se subió justo ahora si excede
|
||||
if (req.file) await fs.unlink(req.file.path).catch(() => {});
|
||||
return res.status(400).json({ error: `Máximo ${MAX_VIDEOS} videos. Elimina uno antes de subir otro.` });
|
||||
}
|
||||
const videos = videoFiles.sort().reverse().map(f => ({ filename: f, url: `/videos/${f}` }));
|
||||
res.json(videos);
|
||||
} catch (err) {
|
||||
console.error('Error uploading video:', err);
|
||||
if (req.file) await fs.unlink(req.file.path).catch(() => {});
|
||||
res.status(500).json({ error: 'Error al subir video' });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/videos/:filename', requireAdmin, async (req, res) => {
|
||||
const { filename } = req.params;
|
||||
if (!filename.startsWith('video-')) {
|
||||
return res.status(400).json({ error: 'Nombre de archivo inválido' });
|
||||
}
|
||||
try {
|
||||
await fs.unlink(path.join(VIDEOS_DIR, filename));
|
||||
res.json({ ok: true });
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') return res.status(404).json({ error: 'Video no encontrado' });
|
||||
console.error('Error deleting video:', err);
|
||||
res.status(500).json({ error: 'Error al eliminar video' });
|
||||
}
|
||||
});
|
||||
|
||||
// Proxy para embeds de redes sociales (evita bloqueo de terceros en móviles)
|
||||
app.get('/api/redes/proxy', async (req, res) => {
|
||||
const { url } = req.query;
|
||||
if (!url) return res.status(400).json({ error: 'Falta url' });
|
||||
|
||||
let embedUrl;
|
||||
if (url.includes('instagram.com/p/')) {
|
||||
const match = url.match(/instagram\.com\/p\/([^/?]+)/);
|
||||
if (!match) return res.status(400).json({ error: 'URL de Instagram inválida' });
|
||||
embedUrl = `https://www.instagram.com/p/${match[1]}/embed`;
|
||||
} else if (url.includes('facebook.com')) {
|
||||
embedUrl = `https://www.facebook.com/plugins/post.php?href=${encodeURIComponent(url)}&show_text=true`;
|
||||
} else {
|
||||
return res.status(400).json({ error: 'Solo se admiten URLs de Instagram y Facebook' });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(embedUrl, {
|
||||
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; InfinityBot)' }
|
||||
});
|
||||
const html = await response.text();
|
||||
res.set('Content-Type', 'text/html; charset=utf-8');
|
||||
res.set('X-Frame-Options', '');
|
||||
res.send(html);
|
||||
} catch (err) {
|
||||
console.error('Redes proxy error:', err);
|
||||
res.status(502).send('Error al cargar el contenido');
|
||||
}
|
||||
});
|
||||
|
||||
// ════════════════════════════════
|
||||
// START
|
||||
// ════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user