diff --git a/functions/communityFunctions.js b/functions/communityFunctions.js new file mode 100644 index 0000000..f6770c1 --- /dev/null +++ b/functions/communityFunctions.js @@ -0,0 +1,82 @@ +import {pool} from '../db.js'; + +export const createNewCommunity = async (req, res) => { + try { + const { nombre, descripcion } = req.body; + const query = await pool.query( + `INSERT INTO comunidad (nombre, descripcion) + VALUES ($1, $2) + RETURNING *`, + [nombre, descripcion] + ); + res.status(201).json(query.rows[0]); + } catch (err) { + console.error('Error creating community:', err); + res.status(500).json({ + error: 'Error al crear la comunidad: ' + err.message + }); + } +} + +export const getCommunityById = async (req, res) => { + try { + const { id_comunidad } = req.params; + const query = await pool.query( + `SELECT * FROM comunidad + WHERE id_comunidad = $1`, + [id_comunidad] + ); + res.json(query.rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + +export const getAllCommunities = async (req, res) => { + try { + const query = await pool.query( + `SELECT * FROM comunidad + ORDER BY nombre ASC` + ); + res.json(query.rows); + } + catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + +export const editCommunity = async (req, res) => { + try { + const { id_comunidad } = req.params; + const { nombre, descripcion } = req.body; + const query = await pool.query( + 'UPDATE comunidad SET nombre = $1, descripcion = $2 WHERE id_comunidad = $3 RETURNING *', + [nombre, descripcion, id_comunidad] + ); + res.json(query.rows[0]); + } + catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + +export const deleteCommunity = async (req, res) => { + try { + const { id_comunidad } = req.params; + const query = await pool.query( + 'DELETE FROM comunidad WHERE id_comunidad = $1 RETURNING *', + [id_comunidad] + ); + res.json(query.rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} \ No newline at end of file diff --git a/functions/offerFunctions.js b/functions/offerFunctions.js new file mode 100644 index 0000000..c016dc7 --- /dev/null +++ b/functions/offerFunctions.js @@ -0,0 +1,87 @@ +import {pool} from '../db.js'; + +export const createNewOffer = async (req, res) => { + try { + const { nombre, descripcion, precio, id_usuario } = req.body; + let imagePath = null; + if (req.processedFilename) { + imagePath = `/uploads/${req.processedFilename}`; + } + const query = await pool.query( + `INSERT INTO oferta (nombre, descripcion, precio, imagen, fecha_publicacion, id_usuario) + VALUES ($1, $2, $3, $4, NOW(), $5) + RETURNING *`, + [nombre, descripcion, precio, imagePath, id_usuario] + ); + res.status(201).json(query.rows[0]); + } catch (err) { + console.error('Error creating offer:', err); + res.status(500).json({ + error: 'Error al crear la oferta: ' + err.message + }); + } +} + +export const getOfferById = async (req, res) => { + try { + const { id_oferta } = req.params; + const query = await pool.query( + `SELECT usuarios.nombre, usuarios.apellido_pa, usuarios.apellido_ma, oferta.* + FROM oferta + JOIN usuarios ON oferta.id_usuario = usuarios.id_usuario + WHERE id_oferta = $1`, + [id_oferta] + ); + res.json(query.rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} +export const getAllOffers = async (req, res) => { + try { + const query = await pool.query( + `SELECT usuarios.nombre, usuarios.apellido_pa, usuarios.apellido_ma, oferta.* + FROM oferta + JOIN usuarios ON oferta.id_usuario = usuarios.id_usuario + ORDER BY oferta.fecha_publicacion DESC` + ); + res.json(query.rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + +export const editOffer = async (req, res) => { + try { + const { id_oferta } = req.params; + const { nombre, descripcion, precio } = req.body; + const query = await pool.query( + 'UPDATE oferta SET nombre = $1, descripcion = $2, precio = $3 WHERE id_oferta = $4 RETURNING *', + [nombre, descripcion, precio, id_oferta] + ); + res.json(query.rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + +export const deleteOffer = async (req, res) => { + try { + const { id_oferta } = req.params; + const query = await pool.query( + 'DELETE FROM oferta WHERE id_oferta = $1 RETURNING *', + [id_oferta] + ); + res.json(query.rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} \ No newline at end of file diff --git a/functions/postFunctions.js b/functions/postFunctions.js index e09b21b..0cc8ede 100644 --- a/functions/postFunctions.js +++ b/functions/postFunctions.js @@ -3,7 +3,7 @@ import {pool} from '../lib/database.js'; export const createNewPost = async (req, res) => { try { - const { contenido, id_usuario } = req.body; + const { contenido, id_usuario, comunidad } = req.body; if (!contenido || !id_usuario) { return res.status(400).json({ @@ -19,10 +19,10 @@ export const createNewPost = async (req, res) => { // Create the post in the database const query = await pool.query( - `INSERT INTO posts (contenido, imagen, id_usuario, fecha_publicacion) - VALUES ($1, $2, $3, NOW()) + `INSERT INTO posts (contenido, imagen, id_usuario, fecha_publicacion, comunidad) + VALUES ($1, $2, $3, NOW(), $4) RETURNING *`, - [contenido, imagePath, id_usuario] + [contenido, imagePath, id_usuario, comunidad] ); // Get the created post with user information @@ -62,6 +62,26 @@ export const getPostById = async (req, res) => { } } +getPostByComunityId = async (req, res) => { + try { + const { comunidad } = req.params; + const query = await pool.query( + `SELECT usuarios.nombre, usuarios.apellido_pa, usuarios.apellido_ma, posts.* + FROM posts + JOIN usuarios ON posts.id_usuario = usuarios.id_usuario + WHERE comunidad = $1 + ORDER BY fecha_publicacion DESC`, + [comunidad] + ); + res.json(query.rows); + } + catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + throw err; + } +} + export const deletePost = async (req, res) => { try { const { id_post } = req.params; diff --git a/routes/communities.js b/routes/communities.js new file mode 100644 index 0000000..697cc11 --- /dev/null +++ b/routes/communities.js @@ -0,0 +1,18 @@ +import {Router} from 'express'; +import { createCommunity, getCommunityById, getAllCommunities, deleteCommunity, editCommunity } from '../functions/communityFunctions.js'; +const router = Router(); + +// Validate create community payload +const validateCommunityPayload = (req, res, next) => { + const { nombre, descripcion } = req.body; + if (!nombre || !descripcion) { + return res.status(400).json({ error: 'nombre and descripcion are required' }); + } + next(); +}; + +router.post('/', validateCommunityPayload, createCommunity); +router.get('/', getAllCommunities); +router.get('/:id_comunidad', getCommunityById); +router.put('/:id_comunidad', validateCommunityPayload, editCommunity); +router.delete('/:id_comunidad', deleteCommunity); \ No newline at end of file diff --git a/routes/offers.js b/routes/offers.js new file mode 100644 index 0000000..aacf618 --- /dev/null +++ b/routes/offers.js @@ -0,0 +1,81 @@ +import { Router } from "express"; +import { createNewOffer, getOfferById, deleteOffer, getAllOffers, getOffersByUserId, editOffer } from "../functions/offerFunctions.js"; +const router = Router(); + +const storage = multer.memoryStorage(); + +const fileFilter = (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Solo se permiten imágenes'), false); + } +}; + +const upload = multer({ + storage: storage, + fileFilter: fileFilter, + limits: { + fileSize: 5 * 1024 * 1024 // 5MB limit + } +}); + +async function processImage(file) { + const imagePath = path.join(process.cwd(), 'uploads'); + + const buffer = file.buffer; + const hash = crypto.createHash('sha256').update(buffer).digest('hex'); + const mime = await fileTypeFromBuffer(buffer).then(type => type?.mime); + + // Validate MIME type + if (!mime || !['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(mime)) { + throw new Error('Unsupported image format'); + } + + // File save name and path if not GIF + let filename; + if (mime === 'image/gif') { + filename = `${hash}.gif`; + await fs.writeFile(path.join(imagePath, filename), buffer); + } else { + filename = `${hash}.webp`; + await sharp(buffer) + .resize({ width: 800, height: 800, fit: 'inside', withoutEnlargement: true }) + .rotate() + .normalise() + .withMetadata() + .toFormat('webp', { quality: 80 }) + .toFile(path.join(imagePath, filename)); + } + return filename; +} + +const processUploadedImage = async (req, res, next) => { + try { + if (req.file) { + const filename = await processImage(req.file); + req.processedFilename = filename; + } + next(); + } catch (error) { + console.error('Error processing image:', error); + res.status(400).json({ error: 'Error al procesar la imagen: ' + error.message }); + } +}; + + +// Validate create offer payload +const validateOfferPayload = (req, res, next) => { + const { nombre, descripcion, precio, id_usuario } = req.body; + if (!nombre || !descripcion || !precio || !id_usuario) { + return res.status(400).json({ error: 'nombre, descripcion, precio and id_usuario are required' }); + } + next(); +}; + +router.post('/', upload.single('imagen'), validateOfferPayload, processUploadedImage, createNewOffer); +router.get('/', getAllOffers); +router.get('/:id_oferta', getOfferById); +router.put('/:id_oferta', upload.single('imagen'), validateOfferPayload, processUploadedImage, editOffer); +router.delete('/:id_oferta', deleteOffer); +router.get('/user/:id_usuario', getOffersByUserId); \ No newline at end of file