diff --git a/backend/index.js b/backend/index.js index 4867368..3117f52 100644 --- a/backend/index.js +++ b/backend/index.js @@ -17,7 +17,7 @@ import { validateToken } from './middleware/auth.js' import { register, login } from './routes/auth.js' import { deleteUser } from './routes/user.js' -import { createOffer, deleteOffer } from './routes/offer.js' +import { createOffer, deleteOffer, getOffers } from './routes/offer.js' /* Test route so nxckwc can test axios*/ app.get('/users', async (req, res) => { @@ -26,12 +26,14 @@ app.get('/users', async (req, res) => { res.json(user) }) + app.post('/login', login) app.post('/register', register) app.delete('/user/:userId', deleteUser) app.post('/crear-oferta', validateToken, createOffer) +app.get('/offers', getOffers) app.delete('/oferta/:projectId', validateToken, deleteOffer) diff --git a/backend/prisma/migrations/20260508093823_url_optional/migration.sql b/backend/prisma/migrations/20260508093823_url_optional/migration.sql new file mode 100644 index 0000000..e28df4b --- /dev/null +++ b/backend/prisma/migrations/20260508093823_url_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Oferta" ALTER COLUMN "url" SET DEFAULT ''; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 9071e53..6e16cc9 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -45,7 +45,7 @@ model Oferta { salario String @default("0") contrato String descripcion String - url String + url String @default("") authorId Int author User @relation(fields: [authorId], references: [id]) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index aafe7bb..134b9bc 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -16,7 +16,7 @@ export const login = async (req, res) => { const user = await prisma.user.findFirst({ where: { email } }); if (!user || !await bcrypt.compare(password, user.password)) { - return res.status(400).json({ error: 'Invalid credentials' }) + return res.status(400).json({ error: 'Credenciales erroneas' }) } // Generate JWT @@ -35,31 +35,32 @@ export const login = async (req, res) => { export const register = async (req, res) => { // Get data - const {username, email, password } = req.body + const {username, email, password, type } = req.body // Check is not empty if (!username || !email || !password) { - return res.status(400).json({ error: 'All fields are required' }) + return res.status(400).json({ error: 'Todos los campos son obligatorios' }) } // Validate data const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ - if (username.length < 3 ||username.length > 14) { return res.status(400).json({ error: 'Username must be between 3 and 14 characters' })} - if (email.length > 30 || !emailRegex.test(email)) { return res.status(400).json({ error: 'Email is not valid' })} - if (password.length < 6 || password.length > 32) { return res.status(400).json({ error: 'Password must be between 6 and 32 characters' })} - + if (username.length < 3 ||username.length > 14) { return res.status(400).json({ error: 'El nombre de usuario debe tener entre 3 y 14 caracteres' })} + if (email.length > 30 || !emailRegex.test(email)) { return res.status(400).json({ error: 'Correo electronico no valido' })} + if (password.length < 6 || password.length > 32) { return res.status(400).json({ error: 'La contraseña debe tener entre 6 y 32 caracteres' })} + if (type !== "candidato" && type !== "reclutador") { return res.status(400).json({ error: 'El tipo de cuenta no es valido' })} + // Check email and username doesnt exists const userExists = await prisma.user.findFirst({ where: { - OR: [ { email }, { username } ] + OR: [ { email } ] } }); // If username or email exists, send error if (userExists) { return res.status(409).json({ - error: 'User already exists' + error: 'Este usuario ya existe' }) } @@ -68,7 +69,7 @@ export const register = async (req, res) => { // Create user const user = await prisma.user.create({ - data: { username, email, password: hashedPassword, type: "candidato" } + data: { username, email, password: hashedPassword, type } }) // Generates token diff --git a/backend/routes/offer.js b/backend/routes/offer.js index 4684db2..ffbbd26 100644 --- a/backend/routes/offer.js +++ b/backend/routes/offer.js @@ -1,15 +1,30 @@ import { prisma } from '../prisma.js' +export const getOffers = async(req, res) => { + const offers = await prisma.oferta.findMany() + + res.json(offers) +} + export const createOffer = async (req, res) => { const { titulo, empresa, ubicacion, salario, contrato, descripcion } = req.body; const authorId = req.user.id if (!titulo || !empresa || !ubicacion || !salario || !contrato || !descripcion) return res.status(400).json({ error: ""}) + if (titulo.length < 3 || titulo.length > 64) { return res.status(400).json({ error: 'El titulo debe tener entre 3 y 64 caracteres' })} + if (empresa.length < 2 || empresa.length > 32) { return res.status(400).json({ error: 'El nombre de la empresa debe tener entre 2 y 32 caracteres' })} + if (ubicacion.length < 3 || ubicacion.length > 16) { return res.status(400).json({ error: 'La ubicacion debe tener entre 3 y 16 caracteres' })} + if (salario.length < 3 || salario.length > 10) { return res.status(400).json({ error: 'El salario debe seguir este formato: 500€ o 20€/hora' })} + if (descripcion.length < 10 || descripcion.length > 256) { return res.status(400).json({ error: 'La descripcion debe tener entre 10 y 256 caracteres' })} + + const contratosValidos = ["freelance", "completo", "medio"] + if (!contratosValidos.includes(contrato)) return res.status(400).json({ error: "contrato no valido" }) + try { const offer = await prisma.oferta.create({ data: { - titulo, empresa, ubicacion, salario, descripcion + titulo, empresa, ubicacion, salario, descripcion, contrato, authorId } }) res.status(201).json(offer) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 6b1445a..2f7f08f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,9 +3,12 @@ import { BrowserRouter, Routes, Route} from 'react-router-dom' import Navbar from './components/Navbar' import Home from './pages/Home' -import NuevaVacante from './pages/NuevaVacante' +import NuevaOferta from './pages/NuevaOferta' import './index.css' import Login from './pages/Login' +import PublicRoute from './components/PublicRoute' +import PrivateRoute from './components/PrivateRoute' +import Register from './pages/Register' function App() { @@ -15,8 +18,9 @@ function App() { } /> - } /> - } /> + } /> + } /> + } /> diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 621033d..5474a25 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -3,7 +3,7 @@ import { AuthContext } from "../context/AuthContext" import { Link } from "react-router-dom" function Navbar() { - const { user } = useContext(AuthContext) + const { user, logout } = useContext(AuthContext) return (