diff --git a/app.js b/app.js index b8629cf..7f761c9 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,8 @@ -const express = require('express') -const path = require('path') - -const authRoutes = require('./routes/auth') -const productsRoutes = require('./routes/products') -const usersRoutes = require('./routes/users') +import express from 'express' +import path from 'path' +import cors from 'cors' +import healthCheckRoutes from './routes/healthcheck' +import authRoutes from './routes/auth' const app = express() const port = process.env.PORT || 3000 @@ -11,14 +10,11 @@ const port = process.env.PORT || 3000 // Middleware app.use(express.json()) // For parsing application/json app.use(express.urlencoded({ extended: true })) // For parsing URL-encoded form data - -// Static Files (Serving Frontend) -app.use(express.static(path.join(__dirname, 'public'))) +app.use(cors()) // Mount Routes +app.use('/healthcheck', healthCheckRoutes) app.use('/auth', authRoutes) -app.use('/products', productsRoutes) -app.use('/users', usersRoutes) // Default Route (Catch-all for undefined routes) app.use((req, res, next) => { diff --git a/package.json b/package.json index b661d5b..9fb3145 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,13 @@ "author": "Your Name", "license": "MIT", "dependencies": { + "bcryptjs": "^3.0.3", + "body-parser": "^2.2.2", + "cors": "^2.8.6", "csv-parser": "^3.0.0", "dotenv": "^16.0.3", - "express": "^4.18.2", + "express": "^4.22.2", + "jsonwebtoken": "^9.0.3", "node-pg": "^1.0.1", "p-limit": "^7.3.0", "pg": "^8.20.0" diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..d0cacf1 --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,70 @@ +import express from 'express' +const { Pool } = require('pg') +import jwt from 'jsonwebtoken' +import bcrypt from 'bcryptjs' + +const router = express.Router() + +// Database connection pool +const pool = new Pool({ + user: process.env.DB_USER, + host: process.env.DB_HOST, + database: process.env.DB_NAME, + password: process.env.DB_PASS, + port: process.env.DB_PORT, +}) + +// Route to handle login and issue JWT token +router.post('/login', async (req, res) => { + const { username, password } = req.body + + try { + const client = await pool.connect() + const queryText = 'SELECT * FROM users WHERE username = $1' + const result = await client.query(queryText, [username]) + + if (!result.rows.length) { + return res.status(401).json({ message: 'Invalid credentials' }) + } + + const user = result.rows[0] + + // Compare passwords + const passwordMatch = await bcrypt.compare(password, user.password) + + if (!passwordMatch) { + return res.status(401).json({ message: 'Invalid credentials' }) + } + + // Issue JWT token + const token = jwt.sign({ id: user.id, username: user.username }, process.env.JWT_SECRET, { expiresIn: '1h' }) + + res.json({ token }) + } catch (error) { + console.error(error) + res.status(500).json({ message: 'Internal server error' }) + } +}) +// Route to refresh JWT token +router.post('/refresh-token', async (req, res) => { + const { refreshToken } = req.body + + if (!refreshToken) { + return res.status(401).json({ message: 'Refresh token is required' }) + } + + try { + // Verify the refresh token + const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET) + + // Create a new access token (JWT) + const token = jwt.sign({ id: decoded.id, username: decoded.username }, process.env.JWT_SECRET, { expiresIn: '1h' }) + + res.json({ token }) + } catch (error) { + console.error(error) + res.status(403).json({ message: 'Invalid refresh token' }) + } +}) +export default router + diff --git a/routes/healthcheck.js b/routes/healthcheck.js new file mode 100644 index 0000000..bcef886 --- /dev/null +++ b/routes/healthcheck.js @@ -0,0 +1,7 @@ +import express from 'express' + +const router = exporess.Router() + +router.get('/', (req, res) => res.json({ status: "UP" })) + +export default router