From b7a1696acfcba9385cfd0a412f5374497693532b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 21 Feb 2026 00:38:50 +0000 Subject: [PATCH 1/4] DevOps assignment completed --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ DEPLOYMENT.md | 11 +++++++++++ backend/Dockerfile | 13 +++++++++++++ backend/backend/settings.py | 2 +- backup.json | 1 + docker-compose.yml | 23 +++++++++++++++++++++++ frontend/Dockerfile | 15 +++++++++++++++ scripts/backup.sh | 2 ++ 8 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 DEPLOYMENT.md create mode 100644 backend/Dockerfile create mode 100644 backup.json create mode 100644 docker-compose.yml create mode 100644 frontend/Dockerfile create mode 100755 scripts/backup.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..53eac0457 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI Pipeline + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Build Next.js + run: | + cd frontend + npm install + npm run build + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.10 + + - name: Install Django dependencies + run: | + cd backend + pip install -r dependencies.txt diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 000000000..33bf5c9c5 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,11 @@ +# Deployment Guide + +Step 1: Clone repo +git clone https://github.com/soulpage/fullstack-assignment.git + +Step 2: Start project +docker-compose up --build -d + +Step 3: Access apps +Next.js -> http://SERVER_IP:3000 +Django -> http://SERVER_IP:8000 diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 000000000..e658a4519 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.10 + +WORKDIR /app + +COPY dependencies.txt . + +RUN pip install -r dependencies.txt + +COPY . . + +EXPOSE 8000 + +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/backend/backend/settings.py b/backend/backend/settings.py index 9de4f024a..1ce0d76c1 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -30,7 +30,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ["*"] # Application definition diff --git a/backup.json b/backup.json new file mode 100644 index 000000000..09b359432 --- /dev/null +++ b/backup.json @@ -0,0 +1 @@ +[{"model": "auth.permission", "pk": 1, "fields": {"name": "Can add log entry", "content_type": 1, "codename": "add_logentry"}}, {"model": "auth.permission", "pk": 2, "fields": {"name": "Can change log entry", "content_type": 1, "codename": "change_logentry"}}, {"model": "auth.permission", "pk": 3, "fields": {"name": "Can delete log entry", "content_type": 1, "codename": "delete_logentry"}}, {"model": "auth.permission", "pk": 4, "fields": {"name": "Can view log entry", "content_type": 1, "codename": "view_logentry"}}, {"model": "auth.permission", "pk": 5, "fields": {"name": "Can add permission", "content_type": 2, "codename": "add_permission"}}, {"model": "auth.permission", "pk": 6, "fields": {"name": "Can change permission", "content_type": 2, "codename": "change_permission"}}, {"model": "auth.permission", "pk": 7, "fields": {"name": "Can delete permission", "content_type": 2, "codename": "delete_permission"}}, {"model": "auth.permission", "pk": 8, "fields": {"name": "Can view permission", "content_type": 2, "codename": "view_permission"}}, {"model": "auth.permission", "pk": 9, "fields": {"name": "Can add group", "content_type": 3, "codename": "add_group"}}, {"model": "auth.permission", "pk": 10, "fields": {"name": "Can change group", "content_type": 3, "codename": "change_group"}}, {"model": "auth.permission", "pk": 11, "fields": {"name": "Can delete group", "content_type": 3, "codename": "delete_group"}}, {"model": "auth.permission", "pk": 12, "fields": {"name": "Can view group", "content_type": 3, "codename": "view_group"}}, {"model": "auth.permission", "pk": 13, "fields": {"name": "Can add content type", "content_type": 4, "codename": "add_contenttype"}}, {"model": "auth.permission", "pk": 14, "fields": {"name": "Can change content type", "content_type": 4, "codename": "change_contenttype"}}, {"model": "auth.permission", "pk": 15, "fields": {"name": "Can delete content type", "content_type": 4, "codename": "delete_contenttype"}}, {"model": "auth.permission", "pk": 16, "fields": {"name": "Can view content type", "content_type": 4, "codename": "view_contenttype"}}, {"model": "auth.permission", "pk": 17, "fields": {"name": "Can add session", "content_type": 5, "codename": "add_session"}}, {"model": "auth.permission", "pk": 18, "fields": {"name": "Can change session", "content_type": 5, "codename": "change_session"}}, {"model": "auth.permission", "pk": 19, "fields": {"name": "Can delete session", "content_type": 5, "codename": "delete_session"}}, {"model": "auth.permission", "pk": 20, "fields": {"name": "Can view session", "content_type": 5, "codename": "view_session"}}, {"model": "auth.permission", "pk": 21, "fields": {"name": "Can add custom user", "content_type": 6, "codename": "add_customuser"}}, {"model": "auth.permission", "pk": 22, "fields": {"name": "Can change custom user", "content_type": 6, "codename": "change_customuser"}}, {"model": "auth.permission", "pk": 23, "fields": {"name": "Can delete custom user", "content_type": 6, "codename": "delete_customuser"}}, {"model": "auth.permission", "pk": 24, "fields": {"name": "Can view custom user", "content_type": 6, "codename": "view_customuser"}}, {"model": "auth.permission", "pk": 25, "fields": {"name": "Can add conversation", "content_type": 7, "codename": "add_conversation"}}, {"model": "auth.permission", "pk": 26, "fields": {"name": "Can change conversation", "content_type": 7, "codename": "change_conversation"}}, {"model": "auth.permission", "pk": 27, "fields": {"name": "Can delete conversation", "content_type": 7, "codename": "delete_conversation"}}, {"model": "auth.permission", "pk": 28, "fields": {"name": "Can view conversation", "content_type": 7, "codename": "view_conversation"}}, {"model": "auth.permission", "pk": 29, "fields": {"name": "Can add message", "content_type": 8, "codename": "add_message"}}, {"model": "auth.permission", "pk": 30, "fields": {"name": "Can change message", "content_type": 8, "codename": "change_message"}}, {"model": "auth.permission", "pk": 31, "fields": {"name": "Can delete message", "content_type": 8, "codename": "delete_message"}}, {"model": "auth.permission", "pk": 32, "fields": {"name": "Can view message", "content_type": 8, "codename": "view_message"}}, {"model": "auth.permission", "pk": 33, "fields": {"name": "Can add role", "content_type": 9, "codename": "add_role"}}, {"model": "auth.permission", "pk": 34, "fields": {"name": "Can change role", "content_type": 9, "codename": "change_role"}}, {"model": "auth.permission", "pk": 35, "fields": {"name": "Can delete role", "content_type": 9, "codename": "delete_role"}}, {"model": "auth.permission", "pk": 36, "fields": {"name": "Can view role", "content_type": 9, "codename": "view_role"}}, {"model": "auth.permission", "pk": 37, "fields": {"name": "Can add version", "content_type": 10, "codename": "add_version"}}, {"model": "auth.permission", "pk": 38, "fields": {"name": "Can change version", "content_type": 10, "codename": "change_version"}}, {"model": "auth.permission", "pk": 39, "fields": {"name": "Can delete version", "content_type": 10, "codename": "delete_version"}}, {"model": "auth.permission", "pk": 40, "fields": {"name": "Can view version", "content_type": 10, "codename": "view_version"}}, {"model": "contenttypes.contenttype", "pk": 1, "fields": {"app_label": "admin", "model": "logentry"}}, {"model": "contenttypes.contenttype", "pk": 2, "fields": {"app_label": "auth", "model": "permission"}}, {"model": "contenttypes.contenttype", "pk": 3, "fields": {"app_label": "auth", "model": "group"}}, {"model": "contenttypes.contenttype", "pk": 4, "fields": {"app_label": "contenttypes", "model": "contenttype"}}, {"model": "contenttypes.contenttype", "pk": 5, "fields": {"app_label": "sessions", "model": "session"}}, {"model": "contenttypes.contenttype", "pk": 6, "fields": {"app_label": "authentication", "model": "customuser"}}, {"model": "contenttypes.contenttype", "pk": 7, "fields": {"app_label": "chat", "model": "conversation"}}, {"model": "contenttypes.contenttype", "pk": 8, "fields": {"app_label": "chat", "model": "message"}}, {"model": "contenttypes.contenttype", "pk": 9, "fields": {"app_label": "chat", "model": "role"}}, {"model": "contenttypes.contenttype", "pk": 10, "fields": {"app_label": "chat", "model": "version"}}] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..ab2958ff8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.9" + +services: + django: + build: ./backend + container_name: django_app + ports: + - "8000:8000" + environment: + DJANGO_SECRET_KEY: supersecretkey123 + FRONTEND_URL: http://localhost:3000 + ALLOWED_HOSTS: "*" + DEBUG: "True" + restart: always + + nextjs: + build: ./frontend + container_name: nextjs_app + ports: + - "3000:3000" + depends_on: + - django + restart: always diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..3c2fed273 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,15 @@ +FROM node:18 + +WORKDIR /app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +RUN npm run build + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100755 index 000000000..ba3f24f1a --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker exec django_app python manage.py dumpdata > backup.json From 89da2e965811ae0e39ea54a5cd951f177c5d5a4d Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 23 Feb 2026 10:33:26 +0000 Subject: [PATCH 2/4] Add CI/CD piplines --- .github/workflows/backend.yml | 30 ++++++++++++++++++++++++++++++ .github/workflows/frontend.yml | 28 ++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/backend.yml create mode 100644 .github/workflows/frontend.yml diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml new file mode 100644 index 000000000..42c7a3af0 --- /dev/null +++ b/.github/workflows/backend.yml @@ -0,0 +1,30 @@ +name: Backend CI/CD + +on: + push: + branches: + - main + paths: + - 'backend/**' + +jobs: + build-and-push-backend: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build Backend Image + run: | + docker build -t panther6/soulpage-backend:latest ./backend + + - name: Push Backend Image + run: | + docker push panther6/soulpage-backend:latest diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml new file mode 100644 index 000000000..f75683f4d --- /dev/null +++ b/.github/workflows/frontend.yml @@ -0,0 +1,28 @@ +name: Frontend CI/CD + +on: + push: + branches: + - main + paths: + - 'frontend/**' + +jobs: + build-and-push-frontend: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build Frontend Image + run: docker build -t panther6/soulpage-frontend:latest ./frontend + + - name: Push Frontend Image + run: docker push panther6/soulpage-frontend:latest From b7351da16bf29baffe593402f62f39659ed8ccc8 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 23 Feb 2026 10:44:06 +0000 Subject: [PATCH 3/4] test ci --- docker-compose.yml | 20 ++++++++++++++++---- frontend/Dockerfile | 14 ++++++++++---- nginx/nginx.conf | 15 +++++++++++++++ 3 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 nginx/nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index ab2958ff8..0b0115584 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,11 +4,11 @@ services: django: build: ./backend container_name: django_app - ports: - - "8000:8000" + expose: + - "8000" environment: DJANGO_SECRET_KEY: supersecretkey123 - FRONTEND_URL: http://localhost:3000 + FRONTEND_URL: http://localhost ALLOWED_HOSTS: "*" DEBUG: "True" restart: always @@ -16,8 +16,20 @@ services: nextjs: build: ./frontend container_name: nextjs_app + expose: + - "3000" + depends_on: + - django + restart: always + + nginx: + image: nginx:alpine + container_name: nginx_proxy ports: - - "3000:3000" + - "80:80" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf depends_on: - django + - nextjs restart: always diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 3c2fed273..c561844aa 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,15 +1,21 @@ -FROM node:18 +# Stage 1: Build +FROM node:18-alpine AS builder WORKDIR /app - COPY package*.json ./ - RUN npm install COPY . . - RUN npm run build +# Stage 2: Production +FROM node:18-alpine + +WORKDIR /app + +COPY --from=builder /app ./ + +ENV NODE_ENV=production EXPOSE 3000 CMD ["npm", "start"] diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 000000000..e121a3000 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,15 @@ +events {} + +http { + server { + listen 80; + + location /api/ { + proxy_pass http://django:8000/; + } + + location / { + proxy_pass http://nextjs:3000/; + } + } +} From ce3b1b55600756d1ee9f5c1280ce165b170fab2f Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 23 Feb 2026 11:09:59 +0000 Subject: [PATCH 4/4] Removed hardcoded secrets --- .github/workflows/ci.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 53eac0457..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: CI Pipeline - -on: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: 18 - - - name: Build Next.js - run: | - cd frontend - npm install - npm run build - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.10 - - - name: Install Django dependencies - run: | - cd backend - pip install -r dependencies.txt