From fa6d6a97bab210dcd9c6b670b374d1364c07f29c Mon Sep 17 00:00:00 2001 From: Joseph Nelson Date: Thu, 14 May 2026 23:43:02 -0700 Subject: [PATCH 1/4] working on healthcheck api and auth api --- app.js | 18 +++++------ package.json | 6 +++- routes/auth.js | 70 +++++++++++++++++++++++++++++++++++++++++++ routes/healthcheck.js | 7 +++++ 4 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 routes/auth.js create mode 100644 routes/healthcheck.js 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 -- 2.52.0 From 6bac3e4e7443fc1905e8f5cbc290b1bdb29dadad Mon Sep 17 00:00:00 2001 From: Joseph Nelson Date: Fri, 15 May 2026 22:32:39 -0700 Subject: [PATCH 2/4] made some basic structure changes and made some tests too --- .gitea/hooks/pre-commit | 20 ----- .gitea/workflows/test.yaml | 26 +++++++ babel.config.js | 3 + package.json | 11 ++- routes/auth.js | 70 ----------------- routes/healthcheck.js | 7 -- app.js => src/app.js | 2 + {config => src/config}/index.js | 0 .../migrations}/001_create-image-table.js | 0 .../002_create-collection-table.js | 0 .../migrations}/003_create-item-table.js | 0 .../migrations}/004_create-location-table.js | 0 .../migrations}/005_create-set-table.js | 0 .../migrations}/006_create-role-table.js | 0 .../migrations}/007_create-user-table.js | 0 src/routes/auth/index.js | 75 +++++++++++++++++++ src/routes/healthcheck/index.js | 7 ++ src/routes/healthcheck/test.js | 19 +++++ 18 files changed, 139 insertions(+), 101 deletions(-) delete mode 100755 .gitea/hooks/pre-commit create mode 100644 .gitea/workflows/test.yaml create mode 100644 babel.config.js delete mode 100644 routes/auth.js delete mode 100644 routes/healthcheck.js rename app.js => src/app.js (97%) rename {config => src/config}/index.js (100%) rename {migrations => src/migrations}/001_create-image-table.js (100%) rename {migrations => src/migrations}/002_create-collection-table.js (100%) rename {migrations => src/migrations}/003_create-item-table.js (100%) rename {migrations => src/migrations}/004_create-location-table.js (100%) rename {migrations => src/migrations}/005_create-set-table.js (100%) rename {migrations => src/migrations}/006_create-role-table.js (100%) rename {migrations => src/migrations}/007_create-user-table.js (100%) create mode 100644 src/routes/auth/index.js create mode 100644 src/routes/healthcheck/index.js create mode 100644 src/routes/healthcheck/test.js diff --git a/.gitea/hooks/pre-commit b/.gitea/hooks/pre-commit deleted file mode 100755 index 4b6a65f..0000000 --- a/.gitea/hooks/pre-commit +++ /dev/null @@ -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 diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..b0221b5 --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -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 ci + + - name: Run linter + run: npm run lint + + - name: Run tests + run: npm test diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..5b4ae09 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +export default { + presets: [['@babel/preset-env', {targets: {node: 'current'}}]], +} diff --git a/package.json b/package.json index 9fb3145..9b0c7fa 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,11 @@ "description": "A basic Express application with PostgreSQL integration.", "main": "app.js", "scripts": { - "start": "node app.js", "dev": "nodemon app.js", "lint": "eslint .", "lint:fix": "eslint . --fix", - "format": "prettier --config prettier.config.js --write .", - "migrate": "node-pg-migrate" + "migrate": "node-pg-migrate", + "test": "jest --forceExit" }, "type": "module", "keywords": [ @@ -34,6 +33,9 @@ "pg": "^8.20.0" }, "devDependencies": { + "@babel/core": "^7.29.0", + "@babel/preset-env": "^7.29.5", + "babel-jest": "^30.4.1", "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.32.0", @@ -41,6 +43,7 @@ "jest": "^29.0.0", "node-pg-migrate": "^8.0.4", "nodemon": "^3.0.0", - "prettier": "^3.8.3" + "prettier": "^3.8.3", + "supertest": "^7.2.2" } } diff --git a/routes/auth.js b/routes/auth.js deleted file mode 100644 index d0cacf1..0000000 --- a/routes/auth.js +++ /dev/null @@ -1,70 +0,0 @@ -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 deleted file mode 100644 index bcef886..0000000 --- a/routes/healthcheck.js +++ /dev/null @@ -1,7 +0,0 @@ -import express from 'express' - -const router = exporess.Router() - -router.get('/', (req, res) => res.json({ status: "UP" })) - -export default router diff --git a/app.js b/src/app.js similarity index 97% rename from app.js rename to src/app.js index 7f761c9..d08097f 100644 --- a/app.js +++ b/src/app.js @@ -24,3 +24,5 @@ app.use((req, res, next) => { app.listen(port, () => { console.log(`Server listening on port ${port}`) }) + +export default app diff --git a/config/index.js b/src/config/index.js similarity index 100% rename from config/index.js rename to src/config/index.js diff --git a/migrations/001_create-image-table.js b/src/migrations/001_create-image-table.js similarity index 100% rename from migrations/001_create-image-table.js rename to src/migrations/001_create-image-table.js diff --git a/migrations/002_create-collection-table.js b/src/migrations/002_create-collection-table.js similarity index 100% rename from migrations/002_create-collection-table.js rename to src/migrations/002_create-collection-table.js diff --git a/migrations/003_create-item-table.js b/src/migrations/003_create-item-table.js similarity index 100% rename from migrations/003_create-item-table.js rename to src/migrations/003_create-item-table.js diff --git a/migrations/004_create-location-table.js b/src/migrations/004_create-location-table.js similarity index 100% rename from migrations/004_create-location-table.js rename to src/migrations/004_create-location-table.js diff --git a/migrations/005_create-set-table.js b/src/migrations/005_create-set-table.js similarity index 100% rename from migrations/005_create-set-table.js rename to src/migrations/005_create-set-table.js diff --git a/migrations/006_create-role-table.js b/src/migrations/006_create-role-table.js similarity index 100% rename from migrations/006_create-role-table.js rename to src/migrations/006_create-role-table.js diff --git a/migrations/007_create-user-table.js b/src/migrations/007_create-user-table.js similarity index 100% rename from migrations/007_create-user-table.js rename to src/migrations/007_create-user-table.js diff --git a/src/routes/auth/index.js b/src/routes/auth/index.js new file mode 100644 index 0000000..23d0690 --- /dev/null +++ b/src/routes/auth/index.js @@ -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 diff --git a/src/routes/healthcheck/index.js b/src/routes/healthcheck/index.js new file mode 100644 index 0000000..961a9de --- /dev/null +++ b/src/routes/healthcheck/index.js @@ -0,0 +1,7 @@ +import express from 'express' + +const router = express.Router() + +router.get('/', (req, res) => res.json({ status: 'UP' })) + +export default router diff --git a/src/routes/healthcheck/test.js b/src/routes/healthcheck/test.js new file mode 100644 index 0000000..99343ad --- /dev/null +++ b/src/routes/healthcheck/test.js @@ -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') + }) +}) -- 2.52.0 From c2cd33e161b872afa637584cd7037205251aabda Mon Sep 17 00:00:00 2001 From: Joseph Nelson Date: Fri, 15 May 2026 22:34:25 -0700 Subject: [PATCH 3/4] fixed install issue --- .gitea/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index b0221b5..e55741b 100644 --- a/.gitea/workflows/test.yaml +++ b/.gitea/workflows/test.yaml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 - name: Install dependencies - run: npm ci + run: npm install --force - name: Run linter run: npm run lint -- 2.52.0 From aaf6c7a60c22efa5accee0b1e3d4de90c39b98ed Mon Sep 17 00:00:00 2001 From: Joseph Nelson Date: Fri, 15 May 2026 22:35:53 -0700 Subject: [PATCH 4/4] cleaned up linting issue --- babel.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/babel.config.js b/babel.config.js index 5b4ae09..de2300b 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ export default { - presets: [['@babel/preset-env', {targets: {node: 'current'}}]], + presets: [['@babel/preset-env', { targets: { node: 'current' } }]], } -- 2.52.0