Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1b2be79
Trying to run the project on my computer installed dependencies
Bahramtotakhil Oct 27, 2025
58d8d12
working on dockerfile for backend
Bahramtotakhil Oct 27, 2025
f40a332
working on dockerfile for frontend
Bahramtotakhil Oct 27, 2025
3a9e925
Added frontend Dockerfile for Next.js app
Bahramtotakhil Oct 27, 2025
72d5b9a
working dockerfile for backend
Bahramtotakhil Oct 27, 2025
9db7739
done with dockerfile for backend
Bahramtotakhil Oct 27, 2025
0dffbe6
started working on docker compose file
Bahramtotakhil Oct 27, 2025
9105160
made progress on docker compose file for front end
Bahramtotakhil Oct 27, 2025
1c948c9
working on docker compose file for backend
Bahramtotakhil Oct 27, 2025
10f915e
Added docker-compose.yml to run database, backend, and frontend conta…
Bahramtotakhil Oct 27, 2025
362baea
Merge branch 'feature-recovery'
Bahramtotakhil Oct 27, 2025
4db7e6e
fix some bugs and configured .yml file to run the project
Bahramtotakhil Nov 2, 2025
225f5f5
made minor changes to files and deplyoyed the project to VM and test it
Bahramtotakhil Nov 14, 2025
9157293
updating the readme file
Bahramtotakhil Nov 14, 2025
f26ed19
updated README.md with Docker-specific setup and deployment instructions
Bahramtotakhil Nov 14, 2025
43aa91a
working on deploy.sh file
Bahramtotakhil Nov 14, 2025
57281c5
working docker deployment Script
Bahramtotakhil Nov 16, 2025
a46adc8
change localhost in fetch
Bahramtotakhil Nov 16, 2025
166d513
minor changes
Bahramtotakhil Nov 16, 2025
8037c13
updating readme file
Bahramtotakhil Nov 16, 2025
e710f32
Sprint 3 done
Bahramtotakhil Nov 16, 2025
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
171 changes: 170 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,173 @@ Restart your development server if it's already running (e.g., npm start).

## Deployment Process
Linked below is the documentation that was created while setting up the virtual machine for deployment.
[Click Here!](https://loving-eye-8b5.notion.site/VM-Deployment-27e101a39e1480328574fee619f042d8)
[Click Here!](https://loving-eye-8b5.notion.site/VM-Deployment-27e101a39e1480328574fee619f042d8)





🐳 Docker Setup

```bash
# Build containers
docker compose build

# Run containers in background
docker compose up -d

# Stop containers
docker compose down

# View logs for all services
docker compose logs -f
```

After running these commands, visit:

Frontend: http://localhost:3000
Backend API: http://localhost:3001/patterns

----

☁️ VM Deployment with Docker

```bash
ssh root@<VM_IP>
# Clone your repo on the VM
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>

# Start containers
docker compose up -d
```

Then open in a browser:
```
http://<VM_IP>:3000
```

----


🌿 Environment Variables for Docker

```
The Docker setup uses:
MySQL service name: db
Backend container: pixel_backend
Frontend container: pixel_frontend
Your docker-compose.yml config for the database:
```

services:
db:
image: mysql:8.0
container_name: pixel_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: pixel2pattern
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql


db.env for Docker:

```
DB_HOST=db
DB_USER=user
DB_PASSWORD=password
DB_NAME=pixel2pattern
DB_DIALECT=mysql
DB_PORT=3306
PORT=3001
```


💾 Database Volume Persistence

A Docker volume is used so MySQL data is not lost when containers restart:

```yaml
volumes:
db-data:
```

Useful commands:

```bash
# List volumes
docker volume ls
```

# Inspect your DB volume
```bash
docker volume inspect <project-folder>_db-data
```
----

🧰 Quick Troubleshooting
Check containers and logs

docker compose ps
docker compose logs -f db
docker compose logs -f backend
docker compose logs -f frontend

Database connection issues

Make sure DB_HOST=db in db.env when using Docker.
Ensure DB_USER, DB_PASSWORD, and DB_NAME match the values in docker-compose.yml.


## 🐚 Docker Deployment Script

To automate VM provisioning and Docker-based deployment, this project includes a bash script named `deploy_docker.sh`.
You can run this script on a **fresh Ubuntu VM** to install everything and start the app.

### ✅ What the Script Does

When you run `deploy_docker.sh`, it:

- Updates the system packages
- Installs Git
- Installs Docker and the Docker Compose plugin
- Configures Docker to start automatically on boot
- Clones the Pixel to Pattern repository from GitHub
- Builds the Docker images and starts all services with `docker compose up -d`
- Prints clear progress messages at each step so you can see what’s happening

After the script completes successfully, the application is available at:
- **Frontend:** `http://<YOUR_VM_IP>:3000`
- **Backend API:** `http://<YOUR_VM_IP>:3001/patterns`
Replace `<YOUR_VM_IP>` with the IP address of your VM.


### ▶️ How to Use `deploy_docker.sh`

1. **SSH into your Ubuntu VM:**

```bash
ssh root@<YOUR_VM_IP>
```
Place the script on the VM
If it’s in the repo already, cd into the repo first. Otherwise, create the file and paste the script contents.
Make the script executable and run it:

```bash
chmod +x deploy_docker.sh
./deploy_docker.sh
```
Verify containers are running:

```bash
docker compose ps
```
You should see the frontend, backend, and db services in the Up state.
Open the app in a browser:
http://<YOUR_VM_IP>:3000
59 changes: 59 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
version: "3.8"

services:
db:
image: mysql:8.0
container_name: pixel_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: pixel2pattern
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-prootpassword"]
interval: 5s
timeout: 5s
retries: 10

backend:
build:
context: ./server
container_name: pixel_backend
restart: always
env_file:
- ./db.env
environment:
DB_HOST: db
DB_USER: user
DB_PASSWORD: password
DB_NAME: pixel2pattern
DB_DIALECT: mysql
PORT: 3001
depends_on:
db:
condition: service_healthy
ports:
- "3001:3001"
command: sh -c "sleep 10 && npm run dev"

frontend:
build:
context: ./pixel2pattern
container_name: pixel_frontend
restart: always
depends_on:
- backend
environment:
- NEXT_PUBLIC_API_URL=http://143.244.187.217:3001
ports:
- "3000:3000"
command: sh -c "npm run dev"


volumes:
db-data:
14 changes: 14 additions & 0 deletions pixel2pattern/dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

EXPOSE 3000
CMD ["npm", "run", "dev"]
34 changes: 18 additions & 16 deletions pixel2pattern/src/app/view/[id]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,26 @@ export default function PatternPage({params}) {
setEditView(true);
}

useEffect(()=> {
const fetchPost = async () => {
try{
const res = await fetch(`http://localhost:3001/patterns/${id}`);
if(!res.ok) throw new Error(`Failed to fetch post with ID: ${id}`);
const post = await res.json();
setPost(post);
setPatternConfig(post.pattern_info);
} catch(err){
console.error('Failed to fetch post, ', err);
useEffect(() => {
const fetchPost = async () => {
try {
const apiBaseUrl =
process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";

const res = await fetch(`${apiBaseUrl}/patterns/${id}`);
if (!res.ok) {
throw new Error(`Failed to fetch post with ID: ${id}`);
}
}

fetchPost();
}, []);
const post = await res.json();
setPost(post);
} catch (err) {
console.error("Failed to fetch post", err);
}
};

fetchPost();
}, [id]);

if (!post) return <Typography>Loading...</Typography>

Expand Down Expand Up @@ -83,7 +88,4 @@ export default function PatternPage({params}) {
</ Box>
);




}
4 changes: 3 additions & 1 deletion pixel2pattern/src/components/EditablePatternView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { useState } from "react";
import { useParams } from "next/navigation";
import PixelDisplay from "@/components/PixelDisplay";

const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";

export default function EditablePatternView({ post, onCancel, params}) {
const { id } = useParams();
const [formData, setFormData] = useState({
Expand All @@ -24,7 +26,7 @@ export default function EditablePatternView({ post, onCancel, params}) {

const handleSubmit = async(e) => {
try{
const res = await fetch(`http://localhost:3001/update/${id}`,
const res = await fetch(`${apiUrl}/update/${id}`,
{
method: 'PATCH',
headers: {"Content-Type": "application/json"},
Expand Down
4 changes: 3 additions & 1 deletion pixel2pattern/src/components/PixelForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import DrawIcon from '@mui/icons-material/Draw';
import {useEffect, useState} from "react";
import { useRouter } from 'next/navigation';

const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";

export default function PixelForm() {
// form detail states
const [name, setName] = useState("");
Expand Down Expand Up @@ -72,7 +74,7 @@ export default function PixelForm() {
}

try{
const res = await fetch('http://localhost:3001/patterns',
const res = await fetch(`${apiUrl}/patterns`,
{
method: "POST",
headers: {"Content-Type": "application/json"},
Expand Down
5 changes: 4 additions & 1 deletion pixel2pattern/src/components/PostsCollection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
import { useEffect, useState } from "react";
import PixelPost from "./PixelPost.jsx";
import Link from "next/link.js";

const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";

export default function PostsCollection() {

const [ pixelPosts, setPixelPosts] = useState([]);

useEffect(() => {
const fetchPosts = async () => {
try {
const res = await fetch("http://localhost:3001/patterns");
const res = await fetch(`${apiUrl}/patterns`);
if (!res.ok) throw new Error("failed to fetch posts");
const posts = await res.json();
setPixelPosts(posts);
Expand Down
Loading