Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d56533d
Setups is ready with working frontend and backend with database
HILAL-VERSE Jun 12, 2026
bbcf966
Completed Database MIgration with Four Tables
HILAL-VERSE Jun 13, 2026
83f0a43
Created and seeded initial dummy data
HILAL-VERSE Jun 18, 2026
6b1fbf8
Developed get venues api with connecting frontend, backend and databade
HILAL-VERSE Jun 18, 2026
d026ece
Developed booking system route
HILAL-VERSE Jun 19, 2026
850f2a7
Added auth with JWT
HILAL-VERSE Jun 20, 2026
74580b2
feat(auth): protect booking routes using custom JWT verification midd…
HILAL-VERSE Jun 20, 2026
0278881
feat: implement user booking history lookup and secure cancellation flow
HILAL-VERSE Jun 20, 2026
315ee97
feat: Added get api for admin that can fetch every bookings
HILAL-VERSE Jun 21, 2026
85fd227
feat: Implemented an API for getting venue by their ID
HILAL-VERSE Jun 21, 2026
ad6256e
feat: Added real signup front end form
HILAL-VERSE Jun 21, 2026
9313b6b
feat completed login and signup front end
HILAL-VERSE Jun 21, 2026
eee8c7c
feat: basic front flow is ready with Home -> signup / login -> role b…
HILAL-VERSE Jun 22, 2026
e4bb325
feat: added booking feature in UI work with booking api (post)
HILAL-VERSE Jun 25, 2026
1231013
feat: Added ui for fetch logges users's bookings and cancel a bookings
HILAL-VERSE Jun 25, 2026
aabea12
feat: Implimented vnues/id API in front end for the brief of each venues
HILAL-VERSE Jun 25, 2026
032b20d
feat/added APIs for owner functions
HILAL-VERSE Jun 26, 2026
1f03219
feat: Added front end features for owner (owner dashboard)
HILAL-VERSE Jun 26, 2026
905de8e
feat:Added API for inserting venue by admin
HILAL-VERSE Jun 26, 2026
aff00d3
feat: added front end from and button in admin panel to add venues by…
HILAL-VERSE Jun 30, 2026
cedadc1
feat: Added ui for owner to see booking of his venues
HILAL-VERSE Jul 1, 2026
70816ad
Ui polished with a consistent visual theme
HILAL-VERSE Jul 1, 2026
fb4edf1
Added all venue route to admin dashboard
HILAL-VERSE Jul 1, 2026
829e67c
feat: Polished home page with a 3d logo instance
HILAL-VERSE Jul 7, 2026
b6e3965
feat:Loacked past dtaes from booking
HILAL-VERSE Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# dependencies
/node_modules

# env files
.env

# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# misc
.DS_Store
14 changes: 14 additions & 0 deletions backend/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { Pool } = require('pg');
require('dotenv').config();
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT || 5432,
});

module.exports = {
query: (text, params) => pool.query(text, params),
connect: () => pool.connect(),
};
56 changes: 56 additions & 0 deletions backend/controllers/loginController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const db = require('../config/db');


const loginController = async (req, res) => {
const {email, password} = req.body;
if(!email || !password){
return res.status(400).json({
message: 'Email and password are required fields'
});
}

try {
const userExist = await db.query('SELECT * FROM users WHERE email = $1', [email]);
if(userExist.rows.length === 0 ){
return res.status(400).json({
message: 'Invalid email or password'
});
}

const user = userExist.rows[0];

const isPasswordValid = await bcrypt.compare(password, user.password_hash);
if(!isPasswordValid){
return res.status(400).json({
message: 'invalid Password'
});
}

//generate jwt
const token = jwt.sign(
{id: user.id, email: user.email, role: user.role},
process.env.JWT_SECRET,
{expiresIn: '24h'}
);

return res.status(200).json({
message: 'Login Succesful',
token,
user: {
id: user.id,
name: user.name,
email: user.email,
role: user.role
}
});
}catch(error){
console.error('Login error', error);
return res.status(500).json({
message: 'Internal Server Error'
});
}
};

module.exports = loginController;
54 changes: 54 additions & 0 deletions backend/controllers/signupController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const db = require('../config/db');


const signupController = async (req, res) => {
const {name, email, password, role} = req.body;
if(!name || !email || !password || !role) {
return res.status(400).json({
message: 'Name, email and password are required fields'
});
}

try {
//check if th user already exists
const existingUser = await db.query('SELECT id FROM users WHERE email = $1', [email]);
if(existingUser.rows.length > 0){
return res.status(400).json({
message: 'An account with this ueamil already exist'
});
}
//hashing the password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);

//save the user to db
const insertQuery = `
INSERT INTO users (name, email, password_hash, role)
VALUES ($1, $2, $3, $4)
RETURNING id, name, email, role, created_at;
`;

const newUser = await db.query(insertQuery, [name,email,hashedPassword,role]);
const user = newUser.rows[0];

//generate jwt
const token = jwt.sign(
{id : user.id, email: user.email, role: user.role},
process.env.JWT_SECRET,
{ expiresIn: '24h'}
);

return res.status(201).json({
message: 'User registered successfully',
token,
user
});
}catch (error) {
console.error('Signup Error:', error);
return res.status(500).json({ error: 'Internal server error.' });
}
}

module.exports = signupController;
60 changes: 60 additions & 0 deletions backend/db/seeders/dummy-seed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// backend/db/seeders/dummy-seed.js
const pool = require('../../config/db');

const seedDatabase = async () => {
try {
console.log('🌱 Starting Dummy Seeding for BookMyVenue...');

// === Seed Users ===
await pool.query(`
INSERT INTO users (name, email, password_hash, role, created_at, updated_at)
VALUES
('Alice Johnson', 'alice@bookmyvenue.com', '$2b$10$dummyhash123456789', 'admin', NOW(), NOW()),
('Bob Smith', 'bob@bookmyvenue.com', '$2b$10$dummyhash123456789', 'user', NOW(), NOW()),
('Charlie Brown', 'charlie@bookmyvenue.com', '$2b$10$dummyhash123456789', 'organizer', NOW(), NOW()),
('Diana Prince', 'diana@bookmyvenue.com', '$2b$10$dummyhash123456789', 'user', NOW(), NOW())
ON CONFLICT (email) DO NOTHING;
`);
console.log('👥 Users seeded');

// === Seed Venues ===
await pool.query(`
INSERT INTO venues (owner_id, name, description, city, address, capacity,
price_per_hour, is_active, is_verified, created_at, updated_at)
VALUES
(1, 'Grand Palace Hall', 'Luxurious AC banquet hall with stage and parking', 'Bangalore', '123 MG Road', 300,
4500, true, true, NOW(), NOW()),
(2, 'Skyline Rooftop', 'Beautiful open rooftop with stunning city view', 'Bangalore', '45 Brigade Road', 150,
3800, true, true, NOW(), NOW()),
(1, 'Green Valley Lawn', 'Spacious outdoor lawn perfect for weddings & events', 'Bangalore', '78 Residency Road', 500,
6500, true, true, NOW(), NOW()),
(3, 'Crystal Ballroom', 'Elegant indoor venue with modern amenities', 'Bangalore', '12 Church Street', 250,
5200, true, true, NOW(), NOW())
ON CONFLICT DO NOTHING;
`);
console.log('📍 Venues seeded');

// === Seed Bookings ===
await pool.query(`
INSERT INTO bookings (user_id, venue_id, start_datetime, end_datetime,
status, total_price, notes, created_at)
VALUES
(2, 1, '2026-07-20 10:00:00', '2026-07-20 14:00:00', 'confirmed', 18000, 'Wedding reception', NOW()),
(3, 2, '2026-07-25 18:00:00', '2026-07-25 22:00:00', 'pending', 15200, 'Corporate event', NOW()),
(4, 3, '2026-08-05 09:00:00', '2026-08-05 13:00:00', 'confirmed', 26000, 'Birthday celebration', NOW())
ON CONFLICT DO NOTHING;
`);
console.log('📅 Bookings seeded');

console.log('✅ Dummy Seeding Completed Successfully! 🎉');

} catch (error) {
console.error('❌ Seeding Failed:', error.message);
}
};

if (require.main === module) {
seedDatabase();
}

module.exports = seedDatabase;
41 changes: 41 additions & 0 deletions backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = 5000;

const healthRouter = require('./routes/health.routes');
const dbRoutes = require('./routes/dbRoutes');
const getVenuesRouter = require('./routes/getVenues');
const bookingRouter = require('./routes/bookingRoutes')
const signupRouter = require('./routes/signupRouter');
const loginRouter = require('./routes/loginRouter');
const cancelBookingRouter = require('./routes/cancelBookingRoute');
const adminBookingRouter = require('./routes/adminBookingsRoute');
const getVenueById = require('./routes/getVenueById');
const ownerApi = require('./routes/ownerRoute');
const adminVenueAdding = require('./routes/addVenueByAdmin');

app.use(cors());
app.use(express.json());


app.use('/health', healthRouter);
app.use('/db', dbRoutes);
app.use('/venues', getVenuesRouter);
app.use('/booking', bookingRouter)
app.use('/signup', signupRouter);
app.use('/login', loginRouter);
app.use('/api', cancelBookingRouter);
app.use('/api', adminBookingRouter);
app.use('/api', getVenueById);
app.use('/api', ownerApi);
app.use('/api', adminVenueAdding);

app.get('/', (req, res) => {
res.send('Hello, World! Your Express server is running.');
});

app.listen(PORT, () => {
console.log(`Server is listening on http://localhost:${PORT}`);
});
29 changes: 29 additions & 0 deletions backend/middlwares/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const jwt = require('jsonwebtoken');

const verifyToken = (req, res, next) => {

const authHeader = req.headers['authorization'];

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
message: 'Access denied. No token provided.'
});
}

const token = authHeader.split(' ')[1];

try {
const decode = jwt.verify(token, process.env.JWT_SECRET);
req.user = decode;

next();
}catch (error) {
return res.status(403).json({
success: false,
message: 'Invalid or expired token'
});
}
};

module.exports = verifyToken;
3 changes: 3 additions & 0 deletions backend/migrations/1781332867517_create-initial-tables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
exports.shorthands = undefined;
exports.up = (pgm) => {};
exports.down = (pgm) => {};
20 changes: 20 additions & 0 deletions backend/migrations/1781333150009_create-users-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Remove all the "export const" lines and use this:
exports.shorthands = undefined;

exports.up = (pgm) => {
pgm.sql(`
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
`);
};

exports.down = (pgm) => {
pgm.sql(`DROP TABLE users;`);
};
25 changes: 25 additions & 0 deletions backend/migrations/1781333679982_create-venues-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Remove all the "export const" lines and use this:
exports.shorthands = undefined;

exports.up = (pgm) => {
pgm.sql(`
CREATE TABLE venues (
id BIGSERIAL PRIMARY KEY,
owner_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
city TEXT NOT NULL,
address TEXT NOT NULL,
capacity INTEGER NOT NULL,
price_per_hour NUMERIC(10,2) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
is_verified BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
`);
};

exports.down = (pgm) => {
pgm.sql(`DROP TABLE venues;`);
};
18 changes: 18 additions & 0 deletions backend/migrations/1781333822529_create-venue-images-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Remove all the "export const" lines and use this:
exports.shorthands = undefined;

exports.up = (pgm) => {
pgm.sql(`
CREATE TABLE venue_images (
id BIGSERIAL PRIMARY KEY,
venue_id BIGINT NOT NULL REFERENCES venues(id) ON DELETE CASCADE,
image_url TEXT NOT NULL,
is_cover BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
`);
};

exports.down = (pgm) => {
pgm.sql(`DROP TABLE venue_images;`);
};
23 changes: 23 additions & 0 deletions backend/migrations/1781333913243_create-bookings-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Remove all the "export const" lines and use this:
exports.shorthands = undefined;

exports.up = (pgm) => {
pgm.sql(`
CREATE TABLE bookings (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
venue_id BIGINT NOT NULL REFERENCES venues(id) ON DELETE CASCADE,
start_datetime TIMESTAMPTZ NOT NULL,
end_datetime TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
total_price NUMERIC(10,2) NOT NULL,
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CHECK (end_datetime > start_datetime)
);
`);
};

exports.down = (pgm) => {
pgm.sql(`DROP TABLE bookings;`);
};
Loading