Skip to content

Commit 376a2fb

Browse files
committed
Initialize professional backend structure with CI/CD and Docker configuration
0 parents  commit 376a2fb

17 files changed

Lines changed: 475 additions & 0 deletions

File tree

.DS_Store

6 KB
Binary file not shown.

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Zappify CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
defaults:
14+
run:
15+
working-directory: backend
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v3
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: 18
25+
cache: 'npm'
26+
cache-dependency-path: backend/package-lock.json
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
- name: Syntax Check
32+
run: node --check server.js
33+
34+
- name: Build Docker image
35+
run: docker build -t zappify-backend .

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
package-lock.json
3+
.env.DS_Store

backend/.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
PORT=5001
2+
MONGO_URI=mongodb+srv://devemishra4_db_user:ZCdrgqQfWJQkhIRQ@cluster0.livvowr.mongodb.net/onlyshes?retryWrites=true&w=majority
3+
JWT_SECRET=onlyshes_secret_key_12345
4+
NODE_ENV=development

backend/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
PORT=5001
2+
MONGO_URI=your_mongodb_atlas_uri_here
3+
JWT_SECRET=your_super_secret_key_here
4+
NODE_ENV=development

backend/Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Use official Node.js runtime as the base image
2+
FROM node:18-alpine as build
3+
4+
# Set working directory inside the container
5+
WORKDIR /app
6+
7+
# Copy package.json and package-lock.json
8+
COPY package*.json ./
9+
10+
# Install dependencies
11+
RUN npm ci
12+
13+
# Copy the rest of the application code
14+
COPY . .
15+
16+
# Final Stage: Lean production image
17+
FROM node:18-alpine
18+
19+
WORKDIR /app
20+
21+
# Copy only relevant files from build stage
22+
COPY --from=build /app .
23+
24+
# Expose the application port
25+
EXPOSE 5001
26+
27+
# Start the application
28+
CMD ["npm", "start"]

backend/config/db.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const mongoose = require('mongoose');
2+
const dotenv = require('dotenv');
3+
4+
dotenv.config();
5+
6+
const connectDB = async () => {
7+
try {
8+
const conn = await mongoose.connect(process.env.MONGO_URI);
9+
console.log(`Connection established with MongoDB: ${conn.connection.host}`);
10+
} catch (error) {
11+
console.error(`Something went wrong: ${error.message}`);
12+
process.exit(1);
13+
}
14+
};
15+
16+
module.exports = connectDB;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const Product = require('../models/Product');
2+
3+
const getProducts = async (req, res) => {
4+
const products = await Product.find({});
5+
res.json(products);
6+
};
7+
8+
const getProductById = async (req, res) => {
9+
const product = await Product.findById(req.params.id);
10+
11+
if (product) {
12+
res.json(product);
13+
} else {
14+
res.status(404).json({ message: 'Product not found' });
15+
}
16+
};
17+
18+
const createProduct = async (req, res) => {
19+
const { name, price, description, image, brand, category, countInStock } = req.body;
20+
21+
const product = new Product({
22+
name,
23+
price,
24+
user: req.user._id,
25+
image,
26+
brand,
27+
category,
28+
countInStock,
29+
description,
30+
});
31+
32+
const createdProduct = await product.save();
33+
res.status(201).json(createdProduct);
34+
};
35+
36+
module.exports = { getProducts, getProductById, createProduct };
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const jwt = require('jsonwebtoken');
2+
const User = require('../models/User');
3+
4+
const generateToken = (id) => {
5+
return jwt.sign({ id }, process.env.JWT_SECRET, {
6+
expiresIn: '30d',
7+
});
8+
};
9+
10+
const authUser = async (req, res) => {
11+
const { email, password } = req.body;
12+
13+
const user = await User.findOne({ email });
14+
15+
if (user && (await user.matchPassword(password))) {
16+
res.json({
17+
_id: user._id,
18+
name: user.name,
19+
email: user.email,
20+
isAdmin: user.isAdmin,
21+
token: generateToken(user._id),
22+
});
23+
} else {
24+
res.status(401).json({ message: 'Invalid email or password' });
25+
}
26+
};
27+
28+
const registerUser = async (req, res) => {
29+
const { name, email, password } = req.body;
30+
31+
if (!email.includes('@') || !email.includes('.')) {
32+
return res.status(400).json({ message: 'Please enter a valid email' });
33+
}
34+
35+
const userExists = await User.findOne({ email });
36+
37+
if (userExists) {
38+
return res.status(400).json({ message: 'User already exists' });
39+
}
40+
41+
const user = await User.create({
42+
name,
43+
email,
44+
password,
45+
});
46+
47+
if (user) {
48+
res.status(201).json({
49+
_id: user._id,
50+
name: user.name,
51+
email: user.email,
52+
isAdmin: user.isAdmin,
53+
token: generateToken(user._id),
54+
});
55+
} else {
56+
res.status(400).json({ message: 'Invalid user data' });
57+
}
58+
};
59+
60+
module.exports = { authUser, registerUser };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const jwt = require('jsonwebtoken');
2+
const User = require('../models/User');
3+
4+
const protect = async (req, res, next) => {
5+
let token;
6+
7+
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
8+
try {
9+
token = req.headers.authorization.split(' ')[1];
10+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
11+
12+
req.user = await User.findById(decoded.id).select('-password');
13+
next();
14+
} catch (error) {
15+
console.error(error);
16+
res.status(401).json({ message: 'Token failed, not authorized' });
17+
}
18+
}
19+
20+
if (!token) {
21+
res.status(401).json({ message: 'No token found, not authorized' });
22+
}
23+
};
24+
25+
const admin = (req, res, next) => {
26+
if (req.user && req.user.isAdmin) {
27+
next();
28+
} else {
29+
res.status(401).json({ message: 'Admin access required' });
30+
}
31+
};
32+
33+
module.exports = { protect, admin };

0 commit comments

Comments
 (0)