working on healthcheck api and auth api #2

Merged
joseph.nelson4456 merged 4 commits from create-auth-routes into main 2026-05-15 22:37:29 -07:00
16 changed files with 151 additions and 36 deletions
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# Check if the lint fix command fails
# If it does, exit with a non-zero status to prevent the commit.
echo "Running lint fix..."
# Replace with your actual lint fix command
# This is just an example, adapt it to your linter and project
npm run lint:fix # e.g., "eslint . --fix"
# Example: Capture the exit code of the command
lint_fix_result=$?
if [ $lint_fix_result -ne 0 ]; then
echo "Linting failed. Commit aborted."
exit 1 # Exit with a non-zero status to prevent the commit
fi
echo "Lint fix completed successfully. Commit allowed."
exit 0 # Exit with a zero status to allow the commit
+26
View File
@@ -0,0 +1,26 @@
name: Test Workflow
on:
pull_request:
branches:
- main
jobs:
test-and-lint:
# Use the label matching your registered runner
runs-on: ubuntu-latest
# Or specify a custom image directly if your runner supports it
container:
image: node:24-alpine
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install dependencies
run: npm install --force
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
+3
View File
@@ -0,0 +1,3 @@
export default {
presets: [['@babel/preset-env', { targets: { node: 'current' } }]],
}
+12 -5
View File
@@ -4,12 +4,11 @@
"description": "A basic Express application with PostgreSQL integration.", "description": "A basic Express application with PostgreSQL integration.",
"main": "app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node app.js",
"dev": "nodemon app.js", "dev": "nodemon app.js",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"format": "prettier --config prettier.config.js --write .", "migrate": "node-pg-migrate",
"migrate": "node-pg-migrate" "test": "jest --forceExit"
}, },
"type": "module", "type": "module",
"keywords": [ "keywords": [
@@ -22,14 +21,21 @@
"author": "Your Name", "author": "Your Name",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bcryptjs": "^3.0.3",
"body-parser": "^2.2.2",
"cors": "^2.8.6",
"csv-parser": "^3.0.0", "csv-parser": "^3.0.0",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"express": "^4.18.2", "express": "^4.22.2",
"jsonwebtoken": "^9.0.3",
"node-pg": "^1.0.1", "node-pg": "^1.0.1",
"p-limit": "^7.3.0", "p-limit": "^7.3.0",
"pg": "^8.20.0" "pg": "^8.20.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0",
"@babel/preset-env": "^7.29.5",
"babel-jest": "^30.4.1",
"eslint": "^10.3.0", "eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
@@ -37,6 +43,7 @@
"jest": "^29.0.0", "jest": "^29.0.0",
"node-pg-migrate": "^8.0.4", "node-pg-migrate": "^8.0.4",
"nodemon": "^3.0.0", "nodemon": "^3.0.0",
"prettier": "^3.8.3" "prettier": "^3.8.3",
"supertest": "^7.2.2"
} }
} }
+9 -11
View File
@@ -1,9 +1,8 @@
const express = require('express') import express from 'express'
const path = require('path') import path from 'path'
import cors from 'cors'
const authRoutes = require('./routes/auth') import healthCheckRoutes from './routes/healthcheck'
const productsRoutes = require('./routes/products') import authRoutes from './routes/auth'
const usersRoutes = require('./routes/users')
const app = express() const app = express()
const port = process.env.PORT || 3000 const port = process.env.PORT || 3000
@@ -11,14 +10,11 @@ const port = process.env.PORT || 3000
// Middleware // Middleware
app.use(express.json()) // For parsing application/json app.use(express.json()) // For parsing application/json
app.use(express.urlencoded({ extended: true })) // For parsing URL-encoded form data app.use(express.urlencoded({ extended: true })) // For parsing URL-encoded form data
app.use(cors())
// Static Files (Serving Frontend)
app.use(express.static(path.join(__dirname, 'public')))
// Mount Routes // Mount Routes
app.use('/healthcheck', healthCheckRoutes)
app.use('/auth', authRoutes) app.use('/auth', authRoutes)
app.use('/products', productsRoutes)
app.use('/users', usersRoutes)
// Default Route (Catch-all for undefined routes) // Default Route (Catch-all for undefined routes)
app.use((req, res, next) => { app.use((req, res, next) => {
@@ -28,3 +24,5 @@ app.use((req, res, next) => {
app.listen(port, () => { app.listen(port, () => {
console.log(`Server listening on port ${port}`) console.log(`Server listening on port ${port}`)
}) })
export default app
+75
View File
@@ -0,0 +1,75 @@
import express from 'express'
import jwt from 'jsonwebtoken'
import bcrypt from 'bcryptjs'
import { database } from '../../config'
const { Pool } = require('pg')
const router = express.Router()
// Database connection pool
const pool = new Pool(database)
// 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
+7
View File
@@ -0,0 +1,7 @@
import express from 'express'
const router = express.Router()
router.get('/', (req, res) => res.json({ status: 'UP' }))
export default router
+19
View File
@@ -0,0 +1,19 @@
// src/routes/healthcheck/test.js
import request from 'supertest'
import app from '../../app'
describe('Health Check Endpoint', () => {
it('should respond with status up', async () => {
const response = await request(app).get('/healthcheck')
expect(response.status).toBe(200)
expect(response.body).toEqual({ status: 'UP' })
})
it('should return a JSON response', async () => {
const response = await request(app).get('/healthcheck')
expect(response.type).toBe('application/json')
})
})