Compare commits

...

3 commits

Author SHA1 Message Date
Kyle Belanger
6535faf82a set up test enviroment 2025-03-05 17:11:41 -05:00
Kyle Belanger
cc728d7324 add app.js 2025-03-05 17:11:32 -05:00
Kyle Belanger
e0b06e3be8 update controllers and models 2025-03-05 17:11:21 -05:00
15 changed files with 265 additions and 153 deletions

7
backend/.env.test Normal file
View file

@ -0,0 +1,7 @@
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=whiskey_collection_test
DB_USER=whiskey_admin
DB_PASSWORD=t2B^IwrR
JWT_SECRET=dsl0ECYSJV999

View file

@ -5,7 +5,10 @@
"scripts": { "scripts": {
"start": "node src/app.js", "start": "node src/app.js",
"dev": "nodemon src/app.js", "dev": "nodemon src/app.js",
"test": "echo \"Error: no test specified\" && exit 1" "prod": "NODE_ENV=production node src/app.js",
"test": "NODE_ENV=test jest --forceExit",
"test:watch": "NODE_ENV=test jest --watch",
"start:test": "NODE_ENV=test node src/app.js"
}, },
"keywords": [ "keywords": [
"whiskey", "whiskey",

57
backend/src/app.js Normal file
View file

@ -0,0 +1,57 @@
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const { sequelize, testConnection } = require('./config/db');
const { Op } = require('sequelize');
// Import routes
const authRoutes = require('./routes/authRoutes');
const whiskeyRoutes = require('./routes/whiskeyRoutes');
const collectionRoutes = require('./routes/collectionRoutes');
const ratingRoutes = require('./routes/ratingRoutes');
// Load environment variables
dotenv.config();
// Initialize app
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/whiskies', whiskeyRoutes);
app.use('/api/collection', collectionRoutes);
app.use('/api/ratings', ratingRoutes);
// Root route
app.get('/', (req, res) => {
res.send('Whiskey Collection API is running');
});
// Sync database and start server
const startServer = async () => {
try {
// Test database connection
await testConnection();
// Sync models with database
await sequelize.sync({ alter: true });
console.log('Database synchronized');
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
} catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
};
startServer();
module.exports = app;

View file

@ -1,7 +1,20 @@
// src/config/db.js
const { Sequelize } = require('sequelize'); const { Sequelize } = require('sequelize');
const dotenv = require('dotenv'); const dotenv = require('dotenv');
const path = require('path');
dotenv.config(); // Load environment-specific variables
if (process.env.NODE_ENV === 'test') {
console.log('Loading TEST environment variables');
dotenv.config({ path: path.resolve(__dirname, '../../.env.test') });
} else {
console.log('Loading DEVELOPMENT environment variables');
dotenv.config();
}
console.log('Environment:', process.env.NODE_ENV);
console.log('Database:', process.env.DB_NAME);
console.log('User:', process.env.DB_USER);
const sequelize = new Sequelize( const sequelize = new Sequelize(
process.env.DB_NAME, process.env.DB_NAME,

View file

@ -0,0 +1,17 @@
const dotenv = require('dotenv');
const path = require('path');
// Load test environment variables
dotenv.config({ path: path.resolve(__dirname, '../../.env.test') });
module.exports = {
testDbConfig: {
database: process.env.DB_NAME,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'postgres',
logging: false
}
};

View file

@ -1,5 +1,5 @@
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const { User } = require('../models/User'); const { User } = require('../models');
const { Op } = require('sequelize'); const { Op } = require('sequelize');
exports.register = async (req, res) => { exports.register = async (req, res) => {

View file

@ -1,4 +1,4 @@
const { Collection, Whiskey } = require('../models/Whiskey'); const { Collection, Whiskey } = require('../models');
// Get user's collection // Get user's collection
exports.getUserCollection = async (req, res) => { exports.getUserCollection = async (req, res) => {

View file

@ -1,4 +1,4 @@
const { Rating, Whiskey, User } = require('../models/Whiskey'); const { Rating, Whiskey, User } = require('../models');
// Get all ratings for a whiskey // Get all ratings for a whiskey
exports.getWhiskeyRatings = async (req, res) => { exports.getWhiskeyRatings = async (req, res) => {

View file

@ -1,4 +1,4 @@
const { Whiskey } = require('../models/Whiskey'); const { Whiskey } = require('../models');
const { Op } = require('sequelize'); const { Op } = require('sequelize');
// Get all whiskies // Get all whiskies

View file

@ -1,23 +1,26 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/db');
const Collection = sequelize.define('Collection', { module.exports = (sequelize) => {
id: { const Collection = sequelize.define('Collection', {
type: DataTypes.INTEGER, id: {
primaryKey: true, type: DataTypes.INTEGER,
autoIncrement: true primaryKey: true,
}, autoIncrement: true
purchaseDate: { },
type: DataTypes.DATE purchaseDate: {
}, type: DataTypes.DATE
purchasePrice: { },
type: DataTypes.FLOAT purchasePrice: {
}, type: DataTypes.FLOAT
notes: { },
type: DataTypes.TEXT notes: {
}, type: DataTypes.TEXT
bottleStatus: { },
type: DataTypes.ENUM('sealed', 'opened', 'empty'), bottleStatus: {
defaultValue: 'sealed' type: DataTypes.ENUM('sealed', 'opened', 'empty'),
} defaultValue: 'sealed'
}); }
});
return Collection;
};

View file

@ -1,42 +1,45 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/db');
const Rating = sequelize.define('Rating', { module.exports = (sequelize) => {
id: { const Rating = sequelize.define('Rating', {
type: DataTypes.INTEGER, id: {
primaryKey: true, type: DataTypes.INTEGER,
autoIncrement: true primaryKey: true,
}, autoIncrement: true
score: { },
type: DataTypes.INTEGER, score: {
allowNull: false, type: DataTypes.INTEGER,
validate: { allowNull: false,
min: 0, validate: {
max: 100 min: 0,
max: 100
}
},
notes: {
type: DataTypes.TEXT
},
nose: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
},
taste: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
},
finish: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
} }
}, });
notes: {
type: DataTypes.TEXT return Rating;
}, };
nose: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
},
taste: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
},
finish: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 10
}
}
});

View file

@ -1,39 +1,43 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/db');
const bcrypt = require('bcrypt');
const User = sequelize.define('User', { module.exports = (sequelize) => {
id: { const User = sequelize.define('User', {
type: DataTypes.INTEGER, id: {
primaryKey: true, type: DataTypes.INTEGER,
autoIncrement: true primaryKey: true,
}, autoIncrement: true
username: { },
type: DataTypes.STRING, username: {
allowNull: false, type: DataTypes.STRING,
unique: true allowNull: false,
}, unique: true
email: { },
type: DataTypes.STRING, email: {
allowNull: false, type: DataTypes.STRING,
unique: true, allowNull: false,
validate: { unique: true,
isEmail: true validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
} }
}, });
password: {
type: DataTypes.STRING, // Add password hashing hook
allowNull: false User.beforeCreate(async (user) => {
} const bcrypt = require('bcrypt');
}, { const salt = await bcrypt.genSalt(10);
hooks: { user.password = await bcrypt.hash(user.password, salt);
beforeCreate: async (user) => { });
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt); // Add password validation method
} User.prototype.validatePassword = async function(password) {
} const bcrypt = require('bcrypt');
}); return await bcrypt.compare(password, this.password);
};
User.prototype.validatePassword = async function(password) {
return await bcrypt.compare(password, this.password); return User;
}; };

View file

@ -0,0 +1,47 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Whiskey = sequelize.define('Whiskey', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
distillery: {
type: DataTypes.STRING,
allowNull: false
},
type: {
type: DataTypes.STRING,
allowNull: false
},
country: {
type: DataTypes.STRING,
allowNull: false
},
region: {
type: DataTypes.STRING
},
age: {
type: DataTypes.INTEGER
},
abv: {
type: DataTypes.FLOAT
},
price: {
type: DataTypes.FLOAT
},
description: {
type: DataTypes.TEXT
},
imageUrl: {
type: DataTypes.STRING
}
});
return Whiskey;
};

View file

@ -1,44 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/db');
const Whiskey = sequelize.define('Whiskey', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
distillery: {
type: DataTypes.STRING,
allowNull: false
},
type: {
type: DataTypes.STRING,
allowNull: false
},
country: {
type: DataTypes.STRING,
allowNull: false
},
region: {
type: DataTypes.STRING
},
age: {
type: DataTypes.INTEGER
},
abv: {
type: DataTypes.FLOAT
},
price: {
type: DataTypes.FLOAT
},
description: {
type: DataTypes.TEXT
},
imageUrl: {
type: DataTypes.STRING
}
});

View file

@ -1,11 +1,14 @@
// src/models/index.js
const { sequelize } = require('../config/db'); const { sequelize } = require('../config/db');
const User = require('./User'); const { DataTypes } = require('sequelize');
const Whiskey = require('./Whiskey');
const Collection = require('./Collection');
const Rating = require('./Rating');
// Define relationships // Initialize models
const User = require('./User')(sequelize);
const Whiskey = require('./Whiskey')(sequelize);
const Collection = require('./Collection')(sequelize);
const Rating = require('./Rating')(sequelize);
// Define associations
User.hasMany(Collection); User.hasMany(Collection);
Collection.belongsTo(User); Collection.belongsTo(User);
@ -18,12 +21,11 @@ Rating.belongsTo(User);
Whiskey.hasMany(Rating); Whiskey.hasMany(Rating);
Rating.belongsTo(Whiskey); Rating.belongsTo(Whiskey);
// Exports // Export models
module.exports = { module.exports = {
User, User,
Whiskey, Whiskey,
Collection, Collection,
Rating, Rating,
sequelize sequelize
}; };