Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
101 changes: 98 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,102 @@
# restaurant-ordering-system
# Restaurant Ordering System 🍽️

Watch Youtube video for set up, run, and demo
This is a containerized and Kubernetes-ready **Restaurant Ordering System** application.

📺 **Watch Setup, Run, and Demo Video:**
https://www.youtube.com/watch?v=Yf8zB4dXp7I

Give a star if you like it!
⭐ **Give a star if you like it!**

---

## 🧰 Features & Technologies

- Frontend (Vue.js) served via Docker on port **80**
- Backend (Node.js/Express) served via Docker on port **8001**
- PostgreSQL database with init script for auto table creation
- Kubernetes manifests for:
- Frontend
- Backend (with HPA)
- Database (with Persistent Volume and Claim)
- Monitoring using Prometheus and Grafana

---

## 📦 Docker Images

- Docker images are available at Docker Hub: [`sharmaaakash170`](https://hub.docker.com/u/sharmaaakash170)

---

## 🚀 Kubernetes Setup

Project structure for Kubernetes manifests:

```
k8s/
├── backend/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── hpa.yaml
├── frontend/
│ ├── deployment.yaml
│ └── service.yaml
├── database/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── pv.yaml
│ └── pvc.yaml
├── init/
│ └── init-db.sql
├── monitoring/
│ ├── grafana/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── prometheus/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── prometheus-config.yaml
```

### Apply Kubernetes Resources

```bash
kubectl apply -f k8s/
```

> Make sure your cluster is up and running and you have access to required Docker images.

---

## 🗂️ Init DB Script

Located at `k8s/init/init-db.sql`
Automatically creates necessary tables when the database pod is started.

---

## 📊 Monitoring

- **Prometheus**: Deployed with its configuration via `prometheus-config.yaml`
- **Grafana**: Deployed with service and deployment manifests

---

## ✅ To-Do (Optional Enhancements)

- Add Ingress controller for external access
- CI/CD integration with GitHub Actions or Jenkins
- Secrets management for DB credentials

---

## 🙌 Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

---

## 📄 License

This project is licensed under the terms of the LICENSE file.

11 changes: 11 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:latest

WORKDIR /app

COPY . .

RUN npm install

RUN npm install prom-client

CMD [ "npm", "start" ]
9 changes: 4 additions & 5 deletions backend/config/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import mysql from "mysql2";
// create the connection to database

const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "db_restaurant"
host: process.env.MYSQL_HOST || "mysql",
user: process.env.MYSQL_USER || "root",
password: process.env.MYSQL_PASSWORD || "root",
database: process.env.MYSQL_DATABASE || "db_restaurant"
});


db.connect(error => {
if (error) throw error;
console.log("Successfully connected to the database.");
Expand Down
98 changes: 56 additions & 42 deletions backend/index.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,81 @@
// using nodemon so that you do not need to type node index.js every time new code saved

// import express - is for building the Rest apis
import express from "express";

// import body-parser - helps to parse the request and create the req.body object
import bodyParser from "body-parser";

// import cors - provides Express middleware to enable CORS with various options, connect frontend
import cors from "cors";

// import routes
import path from "path";
import router from "./routes/routes.js";

// import path
import path from "path";
// Prometheus client setup
import client from "prom-client";

// use path
// Initialize path for static files
const __dirname = path.resolve();

// init express
// Initialize express
const app = express();

// use express json
// Prometheus setup
const register = new client.Registry();
client.collectDefaultMetrics({ register });

// Custom metrics: HTTP Request Counter and Response Time Histogram
const httpRequestCounter = new client.Counter({
name: "http_requests_total",
help: "Total number of HTTP requests",
labelNames: ["method", "route", "status_code"],
});
register.registerMetric(httpRequestCounter);

const responseTimeHistogram = new client.Histogram({
name: "http_response_time_seconds",
help: "HTTP response duration in seconds",
labelNames: ["method", "route"],
});
register.registerMetric(responseTimeHistogram);

// Middleware to track HTTP requests and response times
app.use((req, res, next) => {
const end = responseTimeHistogram.startTimer();
res.on("finish", () => {
// Increment the request counter
httpRequestCounter.labels(req.method, req.path, res.statusCode).inc();
// Track response time
end({ method: req.method, route: req.route?.path || req.path });
});
next();
});

// Middleware setup
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//use cors
app.use(cors());

// use router
app.use(router);
// Metrics endpoint for Prometheus scraping (Make sure this is defined early)
app.get('/metrics', async (req, res) => {
try {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
} catch (err) {
res.status(500).send(err);
}
});

// // Handle production
// if (process.env.NODE_ENV === 'production'){
// // Static folder
// app.use(express.static(__dirname + '/public/'));
// Routes setup (Make sure other routes are defined after /metrics)
app.use(router);

// // Handle SPA
// app.get(/.*/, (req,res)=> res.sendFile(__dirname + '/public/index.html'));
// }
// Static files and SPA (if needed)
app.use(express.static(path.join(__dirname, './restaurant_management/')));

app.get('/api', function(req, res){
res.json({ message: 'Welcome to restaurant api' });
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, './restaurant_management/index.html'));
});

app.use(express.static(path.join(__dirname, './restaurant_management/')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, './restaurant_management/index.html'))
// API Test endpoint (optional)
app.get('/api', (req, res) => {
res.json({ message: 'Welcome to the restaurant API' });
});



// PORT
// Set up server port
const PORT = process.env.PORT || 8001;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});

// https://www.youtube.com/watch?v=GK2TiAAxmQ0
// https://www.bezkoder.com/node-js-rest-api-express-mysql/
// https://www.bezkoder.com/serve-vue-app-express/
// https://www.bezkoder.com/deploy-node-js-app-heroku-cleardb-mysql/
// https://www.youtube.com/watch?v=W-b9KGwVECs
// https://stackoverflow.com/questions/43362014/heroku-no-default-language-could-be-detected-for-this-app-error-thrown-for-no
// https://stackoverflow.com/questions/16128395/what-is-procfile-and-web-and-worker
// https://www.youtube.com/watch?v=lwOsI8LtVEQ
21 changes: 21 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM node:18-alpine AS build

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

FROM nginx:stable-alpine

RUN rm -rf /usr/share/nginx/html/*

COPY --from=build /app/dist /usr/share/nginx/html

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ]
1 change: 1 addition & 0 deletions frontend/src/pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="home-main">
<div class="content">
<span>welcome foodies</span>
<h1>GREEN Deployment</h1>
<h3>Original taste from Mexico 😋</h3>
<p>We guarantee to use fresh food with the best quality. Customers will enjoy Mexican cuisine with
explosive, sophisticated flavors.</p>
Expand Down
4 changes: 2 additions & 2 deletions frontend/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ const path = require('path');
// });

module.exports = {
outputDir: path.resolve(__dirname, '../backend/restaurant_management'),
// outputDir: path.resolve(__dirname, '../backend/restaurant_management'),
devServer: {
proxy: {
'/': {
target: 'http://localhost:8001'
target: 'http://backend:8001'
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions k8s/backend/deployment-blue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-blue
spec:
replicas: 2
selector:
matchLabels:
app: backend
version: blue
template:
metadata:
labels:
app: backend
version: blue
spec:
containers:
- name: backend
image: sharmaaakash170/node-backend:latest
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "200m"
ports:
- containerPort: 8001
env:
- name: MYSQL_HOST
value: "mysql"
- name: MYSQL_ROOT_USER
value: "root"
- name: MYSQL_ROOT_PASSWORD
value: "root"
- name: MYSQL_DATABASE
value: "db_restaurant"

38 changes: 38 additions & 0 deletions k8s/backend/deployment-green.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-green
spec:
replicas: 2
selector:
matchLabels:
app: backend
version: green
template:
metadata:
labels:
app: backend
version: green
spec:
containers:
- name: backend
image: sharmaaakash170/node-backend:new-version
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "200m"
ports:
- containerPort: 8001
env:
- name: MYSQL_HOST
value: "mysql"
- name: MYSQL_ROOT_USER
value: "root"
- name: MYSQL_ROOT_PASSWORD
value: "root"
- name: MYSQL_DATABASE
value: "db_restaurant"

Loading