added user and role APIs and fixed some items that were missing #3
@@ -31,6 +31,12 @@ jobs:
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg DATABASE_URL=${{ secrets.DATABASE_URL }} \
|
||||
--build-arg DB_HOST=${{ secrets.DB_HOST }} \
|
||||
--build-arg DB_USERNAME=${{ secrets.DB_USERNAME }} \
|
||||
--build-arg DB_PASSWORD=${{ secrets.DB_PASSWORD }} \
|
||||
--build-arg DB_PORT=${{ secrets.DB_PORT }} \
|
||||
--build-arg DB_DATABASE=${{ secrets.DB_DATABASE }} \
|
||||
--build-arg JWT_SECRET=${{ secrets.JWT_SECRET }} \
|
||||
-t gitea.nelson-household.com/hard-at-work/tcg-collectors-server/tcg-collectors-server:${{ github.sha }} .
|
||||
docker push gitea.nelson-household.com/hard-at-work/tcg-collectors-server/tcg-collectors-server:${{ github.sha }}
|
||||
|
||||
|
||||
+12
@@ -1,8 +1,20 @@
|
||||
FROM node:24-alpine
|
||||
|
||||
ARG DATABASE_URL
|
||||
ARG DB_HOST
|
||||
ARG DB_USERNAME
|
||||
ARG DB_PASSWORD
|
||||
ARG DB_PORT
|
||||
ARG DB_DATABASE
|
||||
ARG JWT_SECRET
|
||||
|
||||
ENV DATABASE_URL=${DATABASE_URL}
|
||||
ENV DB_HOST=${DB_HOST}
|
||||
ENV DB_USERNAME=${DB_USERNAME}
|
||||
ENV DB_PASSWORD=${DB_PASSWORD}
|
||||
ENV DB_PORT=${DB_PORT}
|
||||
ENV DB_DATABASE=${DB_DATABASE}
|
||||
ENV JWT_SECRET=${JWT_SECRET}
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
@@ -3,6 +3,8 @@ import path from 'path'
|
||||
import cors from 'cors'
|
||||
import healthCheckRoutes from './routes/healthcheck'
|
||||
import authRoutes from './routes/auth'
|
||||
import userRoutes from './routes/users'
|
||||
import roleRoutes from './routes/roles'
|
||||
|
||||
const app = express()
|
||||
const port = process.env.PORT || 3000
|
||||
@@ -15,6 +17,8 @@ app.use(cors())
|
||||
// Mount Routes
|
||||
app.use('/healthcheck', healthCheckRoutes)
|
||||
app.use('/auth', authRoutes)
|
||||
app.use('/users', userRoutes)
|
||||
app.use('/roles', roleRoutes)
|
||||
|
||||
// Default Route (Catch-all for undefined routes)
|
||||
app.use((req, res, next) => {
|
||||
|
||||
@@ -7,4 +7,7 @@ export default {
|
||||
database: process.env.DB_NAME || 'your_db_name',
|
||||
port: process.env.DB_PORT || 5432,
|
||||
},
|
||||
jwtEnv: {
|
||||
secret: process.env.JWT_SECRET || '',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { database } from '../../config'
|
||||
import { database, jwtEnv } from '../../config'
|
||||
|
||||
const { Pool } = require('pg')
|
||||
|
||||
@@ -35,7 +35,7 @@ router.post('/login', async (req, res) => {
|
||||
// Issue JWT token
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, username: user.username },
|
||||
process.env.JWT_SECRET,
|
||||
jwtEnv.secret,
|
||||
{ expiresIn: '1h' }
|
||||
)
|
||||
|
||||
@@ -56,12 +56,12 @@ router.post('/refresh-token', async (req, res) => {
|
||||
|
||||
try {
|
||||
// Verify the refresh token
|
||||
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET)
|
||||
const decoded = jwt.verify(refreshToken, jwtEnv.secret)
|
||||
|
||||
// Create a new access token (JWT)
|
||||
const token = jwt.sign(
|
||||
{ id: decoded.id, username: decoded.username },
|
||||
process.env.JWT_SECRET,
|
||||
jwtEnv.secret,
|
||||
{ expiresIn: '1h' }
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import express from 'express'
|
||||
import { database } from '../../config'
|
||||
|
||||
const { Pool } = require('pg')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// Create a connection pool to the database
|
||||
const pool = new Pool(database)
|
||||
|
||||
// Create a new role
|
||||
router.post('/', async (req, res) => {
|
||||
const { name } = req.body
|
||||
const client = await pool.connect()
|
||||
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
const result = await client.query(
|
||||
'INSERT INTO roles (name, created_at, updated_at) VALUES ($1, NOW(), NOW()) RETURNING *',
|
||||
[name]
|
||||
)
|
||||
await client.query('COMMIT')
|
||||
res.status(201).json(result.rows[0])
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
// Get all roles
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query('SELECT * FROM roles')
|
||||
res.json(result.rows)
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
// Get a single role by id
|
||||
router.get('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const result = await pool.query('SELECT * FROM roles WHERE id = $1', [id])
|
||||
if (result.rows.length === 0)
|
||||
return res.status(404).json({ error: 'Role not found' })
|
||||
res.json(result.rows[0])
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
// Update a role by id
|
||||
router.put('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
const { name } = req.body
|
||||
const client = await pool.connect()
|
||||
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
const result = await client.query(
|
||||
'UPDATE roles SET name = $1, updated_at = NOW() WHERE id = $2 RETURNING *',
|
||||
[name, id]
|
||||
)
|
||||
if (result.rows.length === 0)
|
||||
return res.status(404).json({ error: 'Role not found' })
|
||||
await client.query('COMMIT')
|
||||
res.json(result.rows[0])
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
// Delete a role by id
|
||||
router.delete('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
const client = await pool.connect()
|
||||
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
const result = await client.query('DELETE FROM roles WHERE id = $1', [id])
|
||||
if (result.rows.length === 0)
|
||||
return res.status(404).json({ error: 'Role not found' })
|
||||
await client.query('COMMIT')
|
||||
res.json(result.rows[0])
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,158 @@
|
||||
import express from 'express'
|
||||
import { database } from '../../config'
|
||||
|
||||
const { Pool } = require('pg')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// Create a connection pool to the database
|
||||
const pool = new Pool(database)
|
||||
|
||||
/**
|
||||
* Get all users.
|
||||
*/
|
||||
router.get('/', async (req, res) => {
|
||||
const { rows } = await pool.query('SELECT * FROM users')
|
||||
res.json(rows)
|
||||
})
|
||||
|
||||
/**
|
||||
* Create a new user.
|
||||
*
|
||||
* @param {string} name - The user's name
|
||||
* @param {string} email - The user's email address
|
||||
* @param {string} password - The user's password (must be hashed)
|
||||
* @param {string} role_name - The name of the role to assign to the user
|
||||
*/
|
||||
router.post('/', async (req, res) => {
|
||||
const { name, email, password, role_name } = req.body
|
||||
|
||||
const client = await pool.connect()
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
|
||||
// Query for the UUID of the role by name
|
||||
const roleResult = await client.query(
|
||||
'SELECT id FROM roles WHERE name = $1',
|
||||
[role_name]
|
||||
)
|
||||
if (roleResult.rows.length === 0) {
|
||||
throw new Error(`Role ${role_name} not found`)
|
||||
}
|
||||
const roleId = roleResult.rows[0].id
|
||||
|
||||
// Insert the new user into the database
|
||||
const result = await client.query(
|
||||
'INSERT INTO users (name, email, password, role_id, created_at, updated_at) VALUES ($1, $2, $3, $4, NOW(), NOW()) RETURNING *',
|
||||
[name, email, password, roleId]
|
||||
)
|
||||
|
||||
// Return the newly created user
|
||||
res.status(201).json(result.rows[0])
|
||||
|
||||
await client.query('COMMIT')
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).send(err)
|
||||
} finally {
|
||||
client.release()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Update an existing user.
|
||||
*
|
||||
* @param {number} id - The ID of the user to update
|
||||
* @param {string} name - The new name for the user
|
||||
* @param {string} email - The new email address for the user (optional)
|
||||
* @param {string} password - The new password for the user (optional, must be hashed)
|
||||
* @param {string} role_name - The name of the new role to assign to the user (optional)
|
||||
*/
|
||||
router.put('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
const { name, email, password, role_name } = req.body
|
||||
|
||||
const client = await pool.connect()
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
|
||||
// Query for the UUID of the role by name if provided
|
||||
let roleId = null
|
||||
if (role_name) {
|
||||
const roleResult = await client.query(
|
||||
'SELECT id FROM roles WHERE name = $1',
|
||||
[role_name]
|
||||
)
|
||||
if (roleResult.rows.length === 0) {
|
||||
throw new Error(`Role ${role_name} not found`)
|
||||
}
|
||||
roleId = roleResult.rows[0].id
|
||||
}
|
||||
|
||||
// Update the user in the database
|
||||
let queryText = 'UPDATE users SET name = $1'
|
||||
const values = [name]
|
||||
|
||||
if (email) {
|
||||
queryText += ', email = $2'
|
||||
values.push(email)
|
||||
}
|
||||
|
||||
if (password) {
|
||||
queryText += ', password = $3'
|
||||
values.push(password)
|
||||
}
|
||||
|
||||
if (roleId !== null) {
|
||||
queryText += ', role_id = $4'
|
||||
values.push(roleId)
|
||||
}
|
||||
|
||||
queryText += ' , updated_at = NOW() WHERE id = $5 RETURNING *'
|
||||
values.push(id)
|
||||
|
||||
const result = await client.query(queryText, values)
|
||||
|
||||
// Return the updated user
|
||||
res.json(result.rows[0])
|
||||
|
||||
await client.query('COMMIT')
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).send(err)
|
||||
} finally {
|
||||
client.release()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Delete an existing user.
|
||||
*
|
||||
* @param {number} id - The ID of the user to delete
|
||||
*/
|
||||
router.delete('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const client = await pool.connect()
|
||||
try {
|
||||
await client.query('BEGIN')
|
||||
|
||||
// Delete the user from the database
|
||||
const result = await client.query(
|
||||
'DELETE FROM users WHERE id = $1 RETURNING *',
|
||||
[id]
|
||||
)
|
||||
|
||||
// Return the deleted user
|
||||
res.json(result.rows[0])
|
||||
|
||||
await client.query('COMMIT')
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK')
|
||||
res.status(500).send(err)
|
||||
} finally {
|
||||
client.release()
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user