JIT
Creating a site tour agency with Node.js and MySQL integration
Make a site tour agency with api server on node js and mysql database and itegrate other api

Node.js Tour Agency API with MySQL and External API Integration

I'll create a comprehensive tour agency API system with Node.js, MySQL database, and external API integration. Here's the setup:

<jitProject id="tour-agency-api" title="Tour Agency API with Node.js and MySQL">
  <jitFile type="file" filePath="package.json">
{
  "name": "tour-agency-api",
  "version": "1.0.0",
  "description": "Tour agency API with MySQL and external API integration",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "mysql2": "^3.6.0",
    "sequelize": "^6.35.0",
    "dotenv": "^16.3.1",
    "cors": "^2.8.5",
    "axios": "^1.6.2",
    "jsonwebtoken": "^9.0.2",
    "bcryptjs": "^2.4.3",
    "morgan": "^1.10.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.2"
  }
}
  </jitFile>

  <jitFile type="file" filePath="server.js">
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const { Sequelize } = require('sequelize');
const tourRoutes = require('./routes/tours');
const bookingRoutes = require('./routes/bookings');
const userRoutes = require('./routes/users');
const externalApiRoutes = require('./routes/externalApi');

const app = express();

// Middleware
app.use(cors());
app.use(express.json());
app.use(morgan('dev'));

// Database connection
const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST,
    dialect: 'mysql'
  }
);

// Test DB connection
sequelize.authenticate()
  .then(() => console.log('Database connected'))
  .catch(err => console.error('Unable to connect to the database:', err));

// Routes
app.use('/api/tours', tourRoutes);
app.use('/api/bookings', bookingRoutes);
app.use('/api/users', userRoutes);
app.use('/api/external', externalApiRoutes);

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
  </jitFile>

  <jitFile type="file" filePath=".env">
DB_NAME=tour_agency
DB_USER=root
DB_PASSWORD=your_mysql_password
DB_HOST=localhost
JWT_SECRET=your_jwt_secret_key
WEATHER_API_KEY=your_weather_api_key
CURRENCY_API_KEY=your_currency_api_key
  </jitFile>

  <jitFile type="file" filePath="models/Tour.js">
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');

const Tour = sequelize.define('Tour', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  title: {
    type: DataTypes.STRING,
    allowNull: false
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: false
  },
  location: {
    type: DataTypes.STRING,
    allowNull: false
  },
  duration: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  price: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false
  },
  startDate: {
    type: DataTypes.DATE,
    allowNull: false
  },
  endDate: {
    type: DataTypes.DATE,
    allowNull: false
  },
  maxParticipants: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  imageUrl: {
    type: DataTypes.STRING,
    allowNull: true
  }
}, {
  timestamps: true
});

module.exports = Tour;
  </jitFile>

  <jitFile type="file" filePath="models/Booking.js">
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');

const Booking = sequelize.define('Booking', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  tourId: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  userId: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  bookingDate: {
    type: DataTypes.DATE,
    defaultValue: DataTypes.NOW
  },
  participants: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  totalPrice: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false
  },
  status: {
    type: DataTypes.ENUM('pending', 'confirmed', 'cancelled'),
    defaultValue: 'pending'
  }
}, {
  timestamps: true
});

module.exports = Booking;
  </jitFile>

  <jitFile type="file" filePath="models/User.js">
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const bcrypt = require('bcryptjs');

const User = sequelize.define('User', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false
  },
  email: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true
  },
  password: {
    type: DataTypes.STRING,
    allowNull: false
  },
  role: {
    type: DataTypes.ENUM('user', 'admin'),
    defaultValue: 'user'
  }
}, {
  timestamps: true,
  hooks: {
    beforeCreate: async (user) => {
      user.password = await bcrypt.hash(user.password, 10);
    }
  }
});

User.prototype.validPassword = async function(password) {
  return await bcrypt.compare(password, this.password);
};

module.exports = User;
  </jitFile>

  <jitFile type="file" filePath="config/database.js">
const { Sequelize } = require('sequelize');
require('dotenv').config();

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST,
    dialect: 'mysql'
  }
);

module.exports = sequelize;
  </jitFile>

  <jitFile type="file" filePath="routes/tours.js">
const express = require('express');
const router = express.Router();
const Tour = require('../models/Tour');
const { Op } = require('sequelize');

// Get all tours
router.get('/', async (req, res) => {
  try {
    const tours = await Tour.findAll();
    res.json(tours);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Get single tour
router.get('/:id', async (req, res) => {
  try {
    const tour = await Tour.findByPk(req.params.id);
    if (!tour) {
      return res.status(404).json({ message: 'Tour not found' });
    }
    res.json(tour);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Create tour (admin only)
router.post('/', async (req, res) => {
  try {
    const tour = await Tour.create(req.body);
    res.status(201).json(tour);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// Update tour (admin only)
router.put('/:id', async (req, res) => {
  try {
    const tour = await Tour.findByPk(req.params.id);
    if (!tour) {
      return res.status(404).json({ message: 'Tour not found' });
    }
    await tour.update(req.body);
    res.json(tour);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// Delete tour (admin only)
router.delete('/:id', async (req, res) => {
  try {
    const tour = await Tour.findByPk(req.params.id);
    if (!tour) {
      return res.status(404).json({ message: 'Tour not found' });
    }
    await tour.destroy();
    res.json({ message: 'Tour deleted' });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Search tours
router.get('/search/:query', async (req, res) => {
  try {
    const tours = await Tour.findAll({
      where: {
        [Op.or]: [
          { title: { [Op.like]: `%${req.params.query}%` } },
          { location: { [Op.like]: `%${req.params.query}%` } },
          { description: { [Op.like]: `%${req.params.query}%` } }
        ]
      }
    });
    res.json(tours);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

module.exports = router;
  </jitFile>

  <jitFile type="file" filePath="routes/bookings.js">
const express = require('express');
const router = express.Router();
const Booking = require('../models/Booking');
const Tour = require('../models/Tour');
const User = require('../models/User');
const { authenticate } = require('../middleware/auth');

// Get all bookings (admin only)
router.get('/', authenticate, async (req, res) => {
  try {
    if (req.user.role !== 'admin') {
      return res.status(403).json({ message: 'Unauthorized' });
    }
    const bookings = await Booking.findAll({
      include: [Tour, User]
    });
    res.json(bookings);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Get user's bookings
router.get('/my-bookings', authenticate, async (req, res) => {
  try {
    const bookings = await Booking.findAll({
      where: { userId: req.user.id },
      include: Tour
    });
    res.json(bookings);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Create booking
router.post('/', authenticate, async (req, res) => {
  try {
    const tour = await Tour.findByPk(req.body.tourId);
    if (!tour) {
      return res.status(404).json({ message: 'Tour not found' });
    }

    const booking = await Booking.create({
      tourId: req.body.tourId,
      userId: req.user.id,
      participants: req.body.participants,
      totalPrice: tour.price * req.body.participants
    });

    res.status(201).json(booking);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// Cancel booking
router.put('/:id/cancel', authenticate, async (req, res) => {
  try {
    const booking = await Booking.findByPk(req.params.id);
    if (!booking) {
      return res.status(404).json({ message: 'Booking not found' });
    }

    if (booking.userId !== req.user.id && req.user.role !== 'admin') {
      return res.status(403).json({ message: 'Unauthorized' });
    }

    await booking.update({ status: 'cancelled' });
    res.json(booking);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

module.exports = router;
  </jitFile>

  <jitFile type="file" filePath="routes/users.js">
const express = require('express');
const router = express.Router();
const User = require('../models/*_
Updated files
v1
теперь сделай сайт на html css js и подключеной api сервером

Tour Agency Website with API Integration

I'll create a simple tour agency website that connects to the Node.js API we just set up. Here's the complete implementation:

<jitProject id="tour-agency-website" title="Tour Agency Website with API">
  <jitFile type="file" filePath="index.html">
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Adventure Tours</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <header>
    <div class="container">
      <h1>Adventure Tours</h1>
      <nav>
        <ul>
          <li><a href="#home">Home</a></li>
          <li><a href="#tours">Tours</a></li>
          <li><a href="#about">About</a></li>
          <li><a href="#contact">Contact</a></li>
          <li><button id="loginBtn">Login</button></li>
        </ul>
      </nav>
    </div>
  </header>

  <main>
    <section id="home" class="hero">
      <div class="container">
        <h2>Discover Amazing Destinations</h2>
        <p>Book your next adventure with us and create unforgettable memories.</p>
        <button id="exploreBtn">Explore Tours</button>
      </div>
    </section>

    <section id="tours" class="tours">
      <div class="container">
        <h2>Featured Tours</h2>
        <div id="toursContainer" class="tours-grid"></div>
      </div>
    </section>

    <section id="about" class="about">
      <div class="container">
        <h2>About Us</h2>
        <p>We specialize in creating unique travel experiences around the world.</p>
      </div>
    </section>

    <section id="contact" class="contact">
      <div class="container">
        <h2>Contact Us</h2>
        <form id="contactForm">
          <input type="text" placeholder="Your Name" required>
          <input type="email" placeholder="Your Email" required>
          <textarea placeholder="Your Message" required></textarea>
          <button type="submit">Send Message</button>
        </form>
      </div>
    </section>
  </main>

  <footer>
    <div class="container">
      <p>&copy; 2023 Adventure Tours. All rights reserved.</p>
    </div>
  </footer>

  <div id="loginModal" class="modal">
    <div class="modal-content">
      <span class="close">&times;</span>
      <h2>Login</h2>
      <form id="loginForm">
        <input type="email" id="email" placeholder="Email" required>
        <input type="password" id="password" placeholder="Password" required>
        <button type="submit">Login</button>
      </form>
    </div>
  </div>

  <div id="bookingModal" class="modal">
    <div class="modal-content">
      <span class="close">&times;</span>
      <h2>Book Tour</h2>
      <form id="bookingForm">
        <input type="hidden" id="tourId">
        <input type="text" id="name" placeholder="Full Name" required>
        <input type="email" id="email" placeholder="Email" required>
        <input type="number" id="participants" placeholder="Number of Participants" min="1" required>
        <button type="submit">Confirm Booking</button>
      </form>
    </div>
  </div>

  <script src="script.js"></script>
</body>
</html>
  </jitFile>

  <jitFile type="file" filePath="styles.css">
Updated files
v2
сделай все вместе чтобы запускалось и сделай css и js и server на node js подключи

Complete Tour Agency Website with Node.js Server, MySQL, and Frontend

Here's the complete implementation with all components working together:

// server.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const { Sequelize } = require('sequelize');
const path = require('path');

const app = express();

// Middleware
app.use(cors());
app.use(express.json());
app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));

// Database connection
const sequelize = new Sequelize(
  process.env.DB_NAME || 'tour_agency',
  process.env.DB_USER || 'root',
  process.env.DB_PASSWORD || '',
  {
    host: process.env.DB_HOST || 'localhost',
    dialect: 'mysql'
  }
);

// Models
const Tour = sequelize.define('Tour', {
  id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
  title: { type: Sequelize.STRING, allowNull: false },
  description: { type: Sequelize.TEXT, allowNull: false },
  location: { type: Sequelize.STRING, allowNull: false },
  duration: { type: Sequelize.INTEGER, allowNull: false },
  price: { type: Sequelize.DECIMAL(10, 2), allowNull: false },
  startDate: { type: Sequelize.DATE, allowNull: false },
  endDate: { type: Sequelize.DATE, allowByDefault: false },
  maxParticipants: { type: Sequelize.INTEGER, allowNull: false },
  imageUrl: { type: Sequelize.STRING, allowNull: true }
});

const Booking = sequelize.define('Booking', {
  id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
  tourId: { type: Sequelize.INTEGER, allowNull: false },
  name: { type: Sequelize.STRING, allowNull: false },
  email: { type: Sequelize.STRING, allowNull: false },
  participants: { type: Sequelize.INTEGER, allowNull: false },
  status: { type: Sequelize.ENUM('pending', 'confirmed', 'cancelled'), defaultValue: 'pending' }
});

// Routes
app.get('/api/tours', async (req, res) => {
  try {
    const tours = await Tour.findAll();
    res.json(tours);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/bookings', async (req, res) => {
  try {
    const { tourId, name, email, participants } = req.body;
    const booking = await Booking.create({ tourId, name, email, participants });
    res.status(201).json(booking);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

// Serve frontend
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// Initialize database and start server
async function startServer() {
  try {
    await sequelize.authenticate();
    await sequelize.sync();
    console.log('Database connected and synced');
    
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });
  } catch (err) {
    console.error('Unable to connect to the database:', err);
  }
}

startServer();
// public/script.js
document.addEventListener('DOMContentLoaded', () => {
  // DOM elements
  const toursContainer = document.getElementById('toursContainer');
  const exploreBtn = document.getElementById('exploreBtn');
  const loginBtn = document.getElementById('loginBtn');
  const loginModal = document.getElementById('loginModal');
  const loginForm = document.getElementById('loginForm');
  const bookingModal = document.getElementById('bookingModal');
  const bookingForm = document.getElementById('bookingForm');
  const closeButtons = document.querySelectorAll('.close');

  // Fetch tours from API
  async function fetchTours() {
    try {
      const response = await fetch('/api/tours');
      const tours = await response.json();
      displayTours(tours);
    } catch (err) {
      console.error('Error fetching tours:', err);
    }
  }

  // Display tours
  function displayTours(tours) {
    toursContainer.innerHTML = '';
    tours.forEach(tour => {
      const tourCard = document.createElement('div');
      tourCard.className = 'tour-card';
      tourCard.innerHTML = `
        <img src="${tour.imageUrl || 'https://via.placeholder.com/300'}" alt="${tour.title}">
        <h3>${tour.title}</h3>
        <p>${tour.location} - ${tour.duration} days</p>
        <p>$${tour.price}</p>
        <button class="book-btn" data-id="${tour.id}">Book Now</button>
      `;
      toursContainer.appendChild(tourCard);
    });

    // Add event listeners to book buttons
    document.querySelectorAll('.book-btn').forEach(btn => {
      btn.addEventListener('click', (e) => {
        const tourId = e.target.getAttribute('data-id');
        document.getElementById('tourId').value = tourId;
        bookingModal.style.display = 'block';
      });
    });
  }

  // Handle booking form submission
  bookingForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    const tourId = document.getElementById('tourId').value;
    const name = document.getElementById('name').value;
    const email = document.getElementById('email').value;
    const participants = document.getElementById('participants').value;

    try {
      const response = await fetch('/api/bookings', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ tourId, name, email, participants }),
      });

      if (response.ok) {
        alert('Booking successful!');
        bookingModal.style.display = 'none';
        bookingForm.reset();
      } else {
        const error = await response.json();
        alert(`Error: ${error.error}`);
      }
    } catch (err) {
      alert('Error submitting booking');
      console.error(err);
    }
  });

  // Modal controls
  loginBtn.addEventListener('click', () => {
    loginModal.style.display = 'block';
  });

  exploreBtn.addEventListener('click', () => {
    document.getElementById('tours').scrollIntoView({ behavior: 'smooth' });
  });

  closeButtons.forEach(btn => {
    btn.addEventListener('click', () => {
      loginModal.style.display = 'none';
      bookingModal.style.display = 'none';
    });
  });

  window.addEventListener('click', (e) => {
    if (e.target === loginModal) {
      loginModal.style.display = 'none';
    }
    if (e.target === bookingModal) {
      bookingModal.style.display = 'none';
    }
  });

  // Initialize
  fetchTours();
});
/* public/styles.css */
body {
  font-family: 'Arial', sans-serif;
  margin: 0;
  padding: 0;
  line-height: 1.6;
  color: #333;
}

.container {
  width: 90%;
  max-width: 1200px;
  margin: 0 auto;
}

header {
  background: #2c3e50;
  color: white;
  padding: 1rem 0;
}

header h1 {
  margin: 0;
  font-size: 1.8rem;
}

nav ul {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
}

nav li {
  margin-left: 1rem;
}

nav a {
  color: white;
  text-decoration: none;
}

.hero {
  background: #3498db;
  color: white;
  padding: 3rem 0;
  text-align: center;
}

.tour-card {
  border: 1px solid #ddd;
  border-radius: 5px;
  padding: 1rem;
  margin-bottom: 1rem;
}

.tour-card img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.tour-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1rem;
}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
}

.modal-content {
  background-color: white;
  margin: 10% auto;
  padding: 2rem;
  width: 80%;
  max-width: 500px;
  border-radius: 5px;
}

.close {
  color: #aaa;
  float: right;
  font-size: 1.5rem;
  font-weight: bold;
  cursor: pointer;
}

.close:hover {
  color: black;
}

form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

input, textarea {
  padding: 0.5rem;
  border: 1px solid #ddd;
  border-radius: 4px;
}

button {
  padding: 0.5rem 1rem;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background*_