# oauth github drf svelet docker github action

How to build a user registration system using Django REST Framework (DRF), PostgreSQL, and SvelteKit, leveraging GitHub OAuth2. This is a reasonably complex project, so we'll break it down into manageable steps.

**High-Level Architecture:**

1. **Frontend (SvelteKit):**
    
    * Handles user interface and interaction.
        
    * Redirects users to GitHub for authentication.
        
    * Receives the authorization code after successful login.
        
    * Sends the code to the backend.
        
2. **Backend (DRF):**
    
    * Exchanges the authorization code for an access token with GitHub.
        
    * Fetches user information from GitHub.
        
    * Creates or updates a user in the local database (PostgreSQL).
        
    * Returns a JWT (JSON Web Token) or session cookie for authentication.
        
3. **PostgreSQL:**
    
    * Stores user data and potentially any other relevant information.
        

**Steps:**

**1\. Backend (DRF with PostgreSQL)**

**a. Project Setup:**

```bash
python3 -m venv venv
source venv/bin/activate
pip install django djangorestframework psycopg2 django-cors-headers requests
pip install djangorestframework-simplejwt
django-admin startproject github_auth .
cd github_auth
python manage.py startapp accounts
```

**b.** [`settings.py`](http://settings.py):

```python
# settings.py
import os

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY", "YOUR_SECRET_KEY_HERE")

DEBUG = True  # Set to False in production

ALLOWED_HOSTS = ['*'] # Configure in production

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'accounts',
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'github_auth.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'github_auth.wsgi.application'

# Database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_database_name',
        'USER': 'your_db_user',
        'PASSWORD': 'your_db_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
 }
CORS_ORIGIN_WHITELIST = ['http://localhost:5173']
GITHUB_CLIENT_ID = os.environ.get("GITHUB_CLIENT_ID", "your_github_client_id")
GITHUB_CLIENT_SECRET = os.environ.get("GITHUB_CLIENT_SECRET", "your_github_client_secret")
GITHUB_REDIRECT_URI = "http://localhost:8000/api/accounts/github/callback/"
```

**Make sure you set the** `DATABASE` , `SECRET_KEY`,`GITHUB_CLIENT_ID`,`GITHUB_CLIENT_SECRET` environment variables in your system

**c.** `accounts/`[`models.py`](http://models.py):

```python
# accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    github_id = models.CharField(max_length=255, null=True, blank=True)
    avatar_url = models.URLField(null=True, blank=True)
    bio = models.TextField(null=True, blank=True)
    location = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
       return self.username
```

**d.** `accounts/`[`serializers.py`](http://serializers.py):

```python
# accounts/serializers.py
from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'github_id', 'avatar_url', 'bio', 'location')
        read_only_fields = ('id', 'username', 'email', 'github_id')
```

**e.** `accounts/`[`views.py`](http://views.py):

```python
# accounts/views.py
import requests
from django.conf import settings
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from django.shortcuts import redirect
from .models import User
from .serializers import UserSerializer
from rest_framework_simplejwt.tokens import RefreshToken

@api_view(['GET'])
def github_login(request):
    """Redirect the user to GitHub's authorization endpoint."""
    client_id = settings.GITHUB_CLIENT_ID
    redirect_uri = settings.GITHUB_REDIRECT_URI
    authorize_url = f"https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&scope=user:email"
    return redirect(authorize_url)


@api_view(['GET'])
def github_callback(request):
    """Handle the callback from GitHub after authorization."""
    code = request.GET.get('code')
    if not code:
        return Response({'error': 'Code not provided'}, status=status.HTTP_400_BAD_REQUEST)
    
    token_url = "https://github.com/login/oauth/access_token"
    data = {
        "client_id": settings.GITHUB_CLIENT_ID,
        "client_secret": settings.GITHUB_CLIENT_SECRET,
        "code": code,
        "redirect_uri": settings.GITHUB_REDIRECT_URI
    }

    headers = {'Accept': 'application/json'}
    response = requests.post(token_url, data=data, headers=headers)
    if response.status_code != 200:
         return Response({'error': 'Failed to get access token from Github'}, status=status.HTTP_400_BAD_REQUEST)

    access_token = response.json().get('access_token')

    user_data = fetch_github_user_data(access_token)
    if not user_data:
         return Response({'error': 'Failed to get user data from Github'}, status=status.HTTP_400_BAD_REQUEST)

    user, created = create_or_update_user(user_data)

    if user:
         refresh = RefreshToken.for_user(user)
         return Response({
             'refresh': str(refresh),
             'access': str(refresh.access_token),
             'user':UserSerializer(user).data
            })
    
    return Response({'error': 'Failed to generate JWT Token'}, status=status.HTTP_400_BAD_REQUEST)


def fetch_github_user_data(access_token):
    """Fetch user data from GitHub's API using access token."""
    headers = {'Authorization': f'token {access_token}'}
    response = requests.get("https://api.github.com/user", headers=headers)
    if response.status_code == 200:
       return response.json()
    return None

def create_or_update_user(user_data):
    """Creates or updates a user based on GitHub data."""
    github_id = str(user_data.get('id'))
    username = user_data.get('login')
    email = user_data.get('email', f"{username}@github.com")  # Github public email or create one
    avatar_url = user_data.get('avatar_url')
    bio = user_data.get('bio')
    location = user_data.get('location')
    
    user , created = User.objects.get_or_create(github_id=github_id, defaults={'username': username, 'email': email, 'avatar_url': avatar_url, 'bio':bio , 'location':location })

    if not created:
        user.username = username
        user.email = email
        user.avatar_url = avatar_url
        user.bio = bio
        user.location = location
        user.save()
    
    return user , created
```

**f.** `accounts/`[`urls.py`](http://urls.py):

```python
# accounts/urls.py
from django.urls import path
from .views import github_login, github_callback

urlpatterns = [
    path('github/login/', github_login, name='github_login'),
    path('github/callback/', github_callback, name='github_callback'),
]
```

**g.** `github_auth/`[`urls.py`](http://urls.py):

```python
# github_auth/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/accounts/', include('accounts.urls')),
]
```

**h. Database Migration and Admin User Creation**

```bash
    python manage.py makemigrations
    python manage.py migrate
    python manage.py createsuperuser
```

**2\. Frontend (SvelteKit)**

**a. Project Setup:**

```bash
npm create svelte@latest my-frontend
cd my-frontend
npm install axios dotenv
```

**b.** `.env` File:

```plaintext
VITE_BACKEND_URL=http://localhost:8000
```

**c.** `src/lib/auth.js`:

```javascript
// src/lib/auth.js
import axios from 'axios';

const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;

export const handleGithubLogin = async () => {
     window.location.href = `${BACKEND_URL}/api/accounts/github/login/`
}

export const handleGithubCallback = async (code) => {
     try {
         const response = await axios.get(`${BACKEND_URL}/api/accounts/github/callback/?code=${code}`)
         return response.data
     }catch (error){
         console.log("Callback error:", error)
         return null
     }
}
```

**d.** `src/routes/+page.svelte`:

```svelte
<!-- src/routes/+page.svelte -->
<script>
  import { onMount } from "svelte";
  import { handleGithubLogin, handleGithubCallback } from "$lib/auth";

  let user = null;
  let error = null;

  onMount(async () => {
     const urlParams = new URLSearchParams(window.location.search);
     const code = urlParams.get('code');
       
      if (code){
         const userData = await handleGithubCallback(code);
         if (userData){
             user = userData
         } else {
            error = "Failed to fetch user";
         }
      }
  });
  
</script>

<main>
 {#if error}
     <p style="color:red">{error}</p>
 {:else if user}
   <h1>Welcome, {user.user.username}!</h1>
   <img src={user.user.avatar_url} alt="User Avatar" style="width:100px;height:100px" />
    <p>User ID: {user.user.id}</p>
     <p>Github id: {user.user.github_id}</p>
     <p>Bio: {user.user.bio}</p>
     <p>Location: {user.user.location}</p>
 {:else}
  <button on:click={handleGithubLogin}>Login with GitHub</button>
 {/if}
</main>
```

**e. Run the projects**

```bash
# Backend
cd github_auth
python manage.py runserver
#Frontend
cd my-frontend
npm run dev -- --open
```

**Explanation:**

* **Frontend (SvelteKit):**
    
    * We use `axios` for making HTTP requests to the backend.
        
    * `.env` file to store the backend API URL.
        
    * The `handleGithubLogin` function redirects the user to GitHub for authorization, while `handleGithubCallback` exchanges the `code` returned from GitHub for a token.
        
* **Backend (DRF):**
    
    * The `github_login` view generates and redirects to GitHub authentication URL.
        
    * The `github_callback` view gets the authorization code, gets access token from GitHub, retrieves user data and then creates or updates the user from it.
        
    * The user object is returned using a serializer and token from JWT
        
* **Database:**
    
    * The database setup is in [`settings.py`](http://settings.py)
        
    * User model stores additional information from GitHub.
        
* **Workflow:**
    
    1. The user clicks "Login with GitHub" on the SvelteKit frontend, which redirects them to GitHub.
        
    2. The user authorizes your application on GitHub.
        
    3. GitHub redirects the user back to your SvelteKit application with an authorization code as a query parameter.
        
    4. The frontend sends this code to your DRF backend.
        
    5. The backend exchanges the code for an access token, fetches user data from GitHub, creates or updates a user, and returns a JWT.
        
    6. The frontend can now store the token, and use it for subsequent requests.
        

**Important Notes:**

* **Environment Variables:** Do not store your secret keys directly in the code, use environment variables.
    
* **Security:** Handle security concerns properly, particularly around storing and transmitting tokens.
    
* **CORS:** Configure CORS appropriately in your DRF project based on your frontend's location.
    
* **Error Handling:** Implement comprehensive error handling on both the frontend and backend.
    
* **Production:** Set DEBUG=False and do not use development keys in production.
    
* **User Model Customization:** You can extend the User model with more fields as needed for your application.
    
* **API Security:** Configure permissions and other aspects of your API to fit your needs.
    
* **GitHub Application:** Create a GitHub OAuth Application on the [GitHub Developer Settings](https://github.com/settings/applications/new). You will get the client ID and client secret that you need to input into your backend. Set redirect URL to your backend url `/api/accounts/github/callback/`
    

---

Okay, let's outline how to deploy this application to an Ubuntu server using GitHub Actions. This involves automating the build, test (optional), and deployment process each time you push changes to your repository.

**Prerequisites on Your Ubuntu Server:**

1. **Python 3.x:** Ensure you have Python 3 (and `pip`) installed.
    
2. **PostgreSQL:** PostgreSQL server must be installed, configured with a database user and a database.
    
3. **Git:** Git should be installed.
    
4. **Docker (Optional):** Docker is not required but is highly recommended to containerize both your front and backend applications to simplify the deployment process.
    
5. **Nginx (Optional):** If you use docker you can map ports or you can configure an Nginx reverse proxy for serving both the frontend and the backend application.
    

**Steps:**

1. **Prepare Your Server:**
    
    * **SSH Access:** Ensure you can SSH into your Ubuntu server.
        
    * **Project Folder:** Create a directory on the server where you'll store your project. For example, `/opt/my-app`.
        
    * **Database Setup:** Create the PostgreSQL database and user you configured in your Django settings.
        
    * **Install python dependencies** You will need to install `python3-venv` to create virtual environments.
        
2. **Add** `.github/workflows/deploy.yml` to your repository:
    
    ```yaml
    # .github/workflows/deploy.yml
    name: Deploy to Ubuntu Server
    on:
      push:
        branches:
          - main # Replace main with your main branch
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout Code
            uses: actions/checkout@v4
    
          - name: Set up Python 3.10
            uses: actions/setup-python@v5
            with:
              python-version: 3.10
    
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: 18 # Use the version you want
    
          - name: Install Python Backend Dependencies
            run: |
              cd github_auth
              python -m venv venv
              source venv/bin/activate
              pip install -r requirements.txt
    
          - name: Install Frontend Dependencies
            run: |
              cd my-frontend
              npm install
    
          - name: Build Frontend
            run: |
              cd my-frontend
              npm run build
          
          - name: Deploy to Server via SSH
            uses: appleboy/ssh-action@master
            with:
              host: ${{ secrets.SERVER_IP }}
              username: ${{ secrets.SERVER_USERNAME }}
              key: ${{ secrets.SERVER_PRIVATE_KEY }}
              script: |
                export PROJECT_DIR="/opt/my-app"
                export BACKEND_DIR="$PROJECT_DIR/github_auth"
                export FRONTEND_DIR="$PROJECT_DIR/my-frontend"
                
                echo "Stop Previous Apps"
                sudo pkill -f gunicorn
                
                echo "Pulling latest changes"
                mkdir -p "$PROJECT_DIR"
                cd "$PROJECT_DIR"
                git clone --depth 1 -b ${{ github.ref_name }} https://github.com/${{ github.repository }} .
                
                echo "Set python dependencies and migrate db"
                cd "$BACKEND_DIR"
                python -m venv venv
                source venv/bin/activate
                pip install -r requirements.txt
                python manage.py migrate
                
                echo "Build Frontend"
                cd "$FRONTEND_DIR"
                npm install
                npm run build
    
                echo "Run backend with gunicorn"
                cd "$BACKEND_DIR"
                nohup gunicorn github_auth.wsgi -b 0.0.0.0:8000 &
    
                echo "Run frontend with a static file server"
                cd "$FRONTEND_DIR/dist"
                nohup python -m http.server 5173 &
    ```
    
3. **Configure GitHub Secrets:**
    
    Go to your GitHub repository's settings, then "Secrets and Variables," then "Actions." Add the following secrets: \* `SERVER_IP`: The IP address of your Ubuntu server. \* `SERVER_USERNAME`: The username for SSH access. \* `SERVER_PRIVATE_KEY`: The private key for SSH authentication (be very careful with this).
    
4. **Generate SSH Keys (If you don't have one):**
    
    ```bash
    ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
    ```
    
    Copy the contents of `~/.ssh/id_rsa` as value for the `SERVER_PRIVATE_KEY` in your secrets. Then copy the contents of `~/.ssh/id_`[`rsa.pub`](http://rsa.pub) and append it to `~/.ssh/authorized_keys` file on your server. If the file does not exist create it first.
    
    ```bash
    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    ```
    
    Then you will be able to use the private key to connect to server from your github action workflow.
    
5. **Create** `requirements.txt`: In the root of your `github_auth` app:
    
    ```bash
    pip freeze > requirements.txt
    ```
    
    Commit and push that file to your repository.
    

**Explanation:**

* `on.push.branches`: This triggers the workflow on every push to the main branch.
    
* `runs-on: ubuntu-latest`: The job will run in a virtual Ubuntu machine provided by Github Actions.
    
* `actions/checkout@v4`: The code checkout step.
    
* `actions/setup-python@v5` and `actions/setup-node@v3`: Sets up Python and Node.js environment.
    
* **Install Python/Node Dependencies:** Installs all the required packages to run both the frontend and the backend.
    
* **Build Frontend:** Builds the SvelteKit application.
    
* `appleboy/ssh-action@master`: It connects to your server and executes the commands to deploy the new changes.
    
* **Environment Variables:** Export project directories and use them to navigate the directories.
    
* **Killing existing processes:** Kill the previous python gunicorn application so that we are not running multiple versions at once.
    
* **Pulling Latest Changes:** Pull latest code changes from github.
    
* **Install Python Backend Dependencies:** Install all the python dependencies for backend application.
    
* **Migrating DB:** This will apply all the new migrations to the database.
    
* **Build Frontend** Installs packages and builds the frontend application
    
* **Run Backend with Gunicorn**: We are running a production ready python server for our backend application.
    
* **Run Frontend with http.server**: We are running a static file server that will serve our static files.
    

**Alternative using Docker:**

If you are using Docker, your workflow will have slightly different steps:

1. **Dockerfile for Backend and Frontend:**
    
    * Create a Dockerfile in `github_auth` directory to build backend image
        
    * Create a Dockerfile in `my-frontend` directory to build frontend image.
        
2. **Docker Compose:** Create a `docker-compose.yml` file in the project's root directory to orchestrate both containers.
    
3. **Update** `deploy.yml` to Build and Deploy Docker Images:
    

```yaml
 # .github/workflows/deploy.yml
 name: Deploy to Ubuntu Server
 on:
   push:
     branches:
       - main # Replace main with your main branch
 jobs:
   deploy:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout Code
         uses: actions/checkout@v4

       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v3

       - name: Build and push Docker images
         uses: docker/build-push-action@v5
         with:
           push: false
           build-args: |
               GITHUB_CLIENT_ID=${{ secrets.GITHUB_CLIENT_ID }}
               GITHUB_CLIENT_SECRET=${{ secrets.GITHUB_CLIENT_SECRET }}
               SECRET_KEY=${{ secrets.SECRET_KEY }}
               DB_NAME=${{ secrets.DB_NAME }}
               DB_USER=${{ secrets.DB_USER }}
               DB_PASSWORD=${{ secrets.DB_PASSWORD }}
               DB_HOST=${{ secrets.DB_HOST }}
               DB_PORT=${{ secrets.DB_PORT }}
           tags: |
               ghcr.io/${{ github.repository }}:backend
               ghcr.io/${{ github.repository }}:frontend
           
       - name: Deploy to Server via SSH
         uses: appleboy/ssh-action@master
         with:
           host: ${{ secrets.SERVER_IP }}
           username: ${{ secrets.SERVER_USERNAME }}
           key: ${{ secrets.SERVER_PRIVATE_KEY }}
           script: |
             export PROJECT_DIR="/opt/my-app"
             
             echo "Pulling latest changes"
             mkdir -p "$PROJECT_DIR"
             cd "$PROJECT_DIR"
             git clone --depth 1 -b ${{ github.ref_name }} https://github.com/${{ github.repository }} .
             
             echo "Stop previous running docker containers"
             docker stop $(docker ps -a -q)
             docker rm $(docker ps -a -q)

             echo "Run docker compose"
             docker compose up --build -d
```

4. **Configure GitHub Secrets:**
    
    Go to your GitHub repository's settings, then "Secrets and Variables," then "Actions." Add the following secrets: \* `SERVER_IP`: The IP address of your Ubuntu server. \* `SERVER_USERNAME`: The username for SSH access. \* `SERVER_PRIVATE_KEY`: The private key for SSH authentication (be very careful with this). \* `GITHUB_CLIENT_ID`: Your github application client id. \* `GITHUB_CLIENT_SECRET`: Your github application client secret. \* `SECRET_KEY`: Your django application secret key. \* `DB_NAME`: Your database name. \* `DB_USER`: Your database user name. \* `DB_PASSWORD`: Your database user password. \* `DB_HOST`: Your database host \* `DB_PORT`: Your database port.
    
5. **Add** `docker-compose.yml` file Add `docker-compose.yml` file in the root directory.
    
    ```yaml
    version: '3.8'
    services:
       backend:
          image: ghcr.io/${{ github.repository }}:backend
          restart: always
          ports:
            - "8000:8000"
          environment:
             - SECRET_KEY=${SECRET_KEY}
             - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
             - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
             - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
             - DB_NAME=${DB_NAME}
             - DB_USER=${DB_USER}
             - DB_PASSWORD=${DB_PASSWORD}
             - DB_HOST=${DB_HOST}
             - DB_PORT=${DB_PORT}
          depends_on:
             db:
                 condition: service_healthy
    
       frontend:
          image: ghcr.io/${{ github.repository }}:frontend
          restart: always
          ports:
            - "5173:80"
          depends_on:
             - backend
    
       db:
         image: postgres:15-alpine
         restart: always
         healthcheck:
            test: ["CMD-SHELL", "pg_isready -U postgres"]
            interval: 10s
            timeout: 5s
            retries: 5
         environment:
           - POSTGRES_USER=postgres
           - POSTGRES_PASSWORD=${DB_PASSWORD}
           - POSTGRES_DB=${DB_NAME}
         ports:
           - "5432:5432"
    ```
    

**Key Points with Docker:**

* **Docker Images:** The `docker/build-push-action` step builds Docker images.
    
* **Environment Variables:** Environment variables are passed as build arguments.
    
* `docker-compose` This is the main file that manages all the services, including database.
    
* **Cleanup:** The script stop and remove old containers before run.
    
* **Deployment:** The workflow pushes images to the GitHub Container Registry and uses `docker compose up` to create and start container on the server.
    

**Important Notes:**

* **Testing:** Consider adding unit and integration testing steps to your workflow before deploying.
    
* **Server Security:** Ensure your server is secured correctly, with firewalls and other security measures.
    
* **Rolling Back:** Plan a strategy to roll back in case of failed deployments.
    
* **Monitoring:** After deploying, set up monitoring to keep track of the performance of your system.
    
* **Nginx:** If you deploy without docker, you can use Nginx to reverse proxy both the frontend and backend so they run on port 80 or 443.
    

This detailed guide should provide you with a solid foundation for deploying your application using GitHub Actions. Remember to adjust the steps to match your project's specific needs and your security practices. Let me know if you need further clarifications or details on any of these points!

---

Okay, let's adjust the deployment instructions to use Docker and Caddy on your Ubuntu server. This setup leverages Caddy as a reverse proxy, simplifying the routing to your Dockerized applications.

**Prerequisites on Your Ubuntu Server:**

1. **Docker:** Docker and Docker Compose must be installed.
    
2. **Caddy:** Caddy web server should be installed.
    
3. **Your Application:** Your project should be in a Git repository, and you should have a `docker-compose.yml` at the root of your repository as configured in previous answer.
    

**Revised Steps:**

1. **Prepare your Ubuntu Server:**
    
    * **SSH Access:** Ensure you can SSH into your Ubuntu server.
        
    * **Project Directory:** Create a directory, such as `/opt/my-app`, where you'll clone your project.
        
    * **Docker Setup:** Ensure Docker and Docker Compose are installed and running.
        
    * **Caddyfile:** Create a `Caddyfile` that reverse proxies traffic to your docker containers.
        
2. **Update GitHub Actions Workflow:** Update your `.github/workflows/deploy.yml` file to use Docker, and Caddyfile will be setup manually on the server.
    

```yaml
    # .github/workflows/deploy.yml
    name: Deploy to Ubuntu Server
    on:
      push:
        branches:
          - main # Replace main with your main branch
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout Code
            uses: actions/checkout@v4

          - name: Set up Docker Buildx
            uses: docker/setup-buildx-action@v3

          - name: Build and push Docker images
            uses: docker/build-push-action@v5
            with:
              push: true
              build-args: |
                  GITHUB_CLIENT_ID=${{ secrets.GITHUB_CLIENT_ID }}
                  GITHUB_CLIENT_SECRET=${{ secrets.GITHUB_CLIENT_SECRET }}
                  SECRET_KEY=${{ secrets.SECRET_KEY }}
                  DB_NAME=${{ secrets.DB_NAME }}
                  DB_USER=${{ secrets.DB_USER }}
                  DB_PASSWORD=${{ secrets.DB_PASSWORD }}
                  DB_HOST=${{ secrets.DB_HOST }}
                  DB_PORT=${{ secrets.DB_PORT }}
              tags: |
                  ghcr.io/${{ github.repository }}:backend
                  ghcr.io/${{ github.repository }}:frontend

          - name: Deploy to Server via SSH
            uses: appleboy/ssh-action@master
            with:
              host: ${{ secrets.SERVER_IP }}
              username: ${{ secrets.SERVER_USERNAME }}
              key: ${{ secrets.SERVER_PRIVATE_KEY }}
              script: |
                export PROJECT_DIR="/opt/my-app"

                echo "Pulling latest changes"
                mkdir -p "$PROJECT_DIR"
                cd "$PROJECT_DIR"
                git clone --depth 1 -b ${{ github.ref_name }} https://github.com/${{ github.repository }} .

                echo "Stop previous running docker containers"
                docker stop $(docker ps -a -q)
                docker rm $(docker ps -a -q)

                echo "Run docker compose"
                docker compose up --build -d
```

3. **Configure GitHub Secrets:**
    
    Go to your GitHub repository's settings, then "Secrets and Variables," then "Actions." Add the following secrets: \* `SERVER_IP`: The IP address of your Ubuntu server. \* `SERVER_USERNAME`: The username for SSH access. \* `SERVER_PRIVATE_KEY`: The private key for SSH authentication (be very careful with this). \* `GITHUB_CLIENT_ID`: Your github application client id. \* `GITHUB_CLIENT_SECRET`: Your github application client secret. \* `SECRET_KEY`: Your django application secret key. \* `DB_NAME`: Your database name. \* `DB_USER`: Your database user name. \* `DB_PASSWORD`: Your database user password. \* `DB_HOST`: Your database host \* `DB_PORT`: Your database port.
    
4. **Caddyfile Configuration:**
    
    Create a `Caddyfile` on your server, typically in `/etc/caddy/`. Example:
    
    ```plaintext
    yourdomain.com {
        reverse_proxy /api/* backend:8000
        reverse_proxy * frontend:80
    }
    ```
    
    * [`yourdomain.com`](http://yourdomain.com): Replace this with your actual domain or subdomain.
        
    * `reverse_proxy /api/* backend:8000`: Routes requests to `/api/*` to the backend docker container.
        
    * `reverse_proxy * frontend:80`: Routes all other traffic to the frontend docker container.
        
5. **Enable Caddy:** Start and enable Caddy with
    
    ```bash
       sudo systemctl start caddy
       sudo systemctl enable caddy
    ```
    

**Explanation:**

* **Workflow Changes:**
    
    * The workflow now only builds and pushes docker images to Github Container Registry, then uses SSH to connect to server, pull new code changes, stop and remove old containers and run docker-compose.
        
* **Caddy Reverse Proxy:**
    
    * Caddy handles routing requests to your frontend and backend containers based on paths. It also automatically manages TLS certificates for your domain, making the process easy.
        
* **Docker:** Both frontend and backend applications are containerized using docker.
    

**Key Advantages:**

* **Simpler Deployment:** Deployment is now a simple `git push` operation.
    
* **Automated Infrastructure:** Your infrastructure is defined as code (`docker-compose.yml` & `Caddyfile`)
    
* **TLS/SSL:** Caddy automatically provisions HTTPS certificates for your domain.
    
* **Scalable:** Easier to scale your application by adding more containers.
    

**Workflow:**

1. You push code to GitHub.
    
2. GitHub Actions builds new Docker images and pushes them to the Github Container Registry.
    
3. The SSH action runs the script that clones new changes to the server, stops and remove old containers.
    
4. The script runs `docker compose up` that creates new containers based on new docker image.
    
5. Caddy automatically detect and route traffic to new containers.
    

**Important Notes:**

* **Domain DNS:** Make sure the domain name used in Caddyfile points to your server's IP.
    
* **Caddy Setup:** Ensure Caddy is installed on the server with the required permissions.
    
* **Environment Variables:** The `.env` variables are passed as build arguments, and as part of `docker-compose.yml`
    
* **Docker Compose:** The `docker-compose.yml` orchestrates all of the services and networking for your application.
    
* **Error Handling:** Add proper error logging and monitoring.
    

With these revisions, you will now be able to use Docker, Caddy, and GitHub Actions to automate the deployment of your application.

---

**Project Mission Summary:**

Our goal is to create a user registration and authentication system using the following technologies:

* **Frontend:** SvelteKit
    
    * Handles the user interface and user interactions.
        
    * Initiates the GitHub OAuth2 authentication flow.
        
    * Receives authorization codes and communicates with the backend.
        
* **Backend:** Django REST Framework (DRF)
    
    * Exchanges GitHub authorization codes for access tokens.
        
    * Fetches user information from GitHub's API.
        
    * Creates or updates user data in a PostgreSQL database.
        
    * Generates JSON Web Tokens (JWTs) for authentication.
        
* **Database:** PostgreSQL
    
    * Persistently stores user data and other application-related information.
        
* **Deployment:**
    
    * Automate deployment to an Ubuntu server using GitHub Actions.
        
    * Leverage Docker and Docker Compose for containerization.
        
    * Utilize Caddy as a reverse proxy for secure traffic routing.
        

**Core Functionality:**

1. **GitHub OAuth2 Login:** Users can register or log in using their GitHub accounts.
    
2. **User Data Management:** The system stores basic user information retrieved from GitHub in the PostgreSQL database.
    
3. **Secure Authentication:** JWTs enable authenticated access to backend resources for logged-in users.
    
4. **Automated Deployment:** Continuous integration and continuous delivery (CI/CD) through GitHub Actions, containerized with Docker, reverse proxied with Caddy.
    

**Key Technologies and Their Purpose:**

* **SvelteKit:** For building a fast, modern, and interactive user interface.
    
* **Django REST Framework (DRF):** For creating a robust, flexible, and well-documented RESTful API.
    
* **PostgreSQL:** For reliable, scalable, and efficient data storage.
    
* **GitHub OAuth2:** For enabling secure third-party authentication.
    
* **JSON Web Tokens (JWT):** For secure token-based authentication.
    
* **GitHub Actions:** For automating the build, test, and deployment process.
    
* **Docker:** For containerizing the application for consistent and portable deployments.
    
* **Caddy:** For automatically managing TLS certificates and acting as a reverse proxy.
    

**Resources for Further Reading:**

Here are some resources categorized by technology to deepen your understanding:

**SvelteKit:**

* **Official SvelteKit Documentation:** [https://kit.svelte.dev/](https://kit.svelte.dev/) - Comprehensive guide and API reference.
    
* **Svelte Tutorial:** [https://svelte.dev/tutorial](https://svelte.dev/tutorial) - Interactive tutorial for learning the Svelte framework.
    
* **SvelteKit GitHub:** [https://github.com/sveltejs/kit](https://github.com/sveltejs/kit) - SvelteKit's source code, issues, and discussions.
    

**Django REST Framework (DRF):**

* **Official DRF Documentation:** [https://www.django-rest-framework.org/](https://www.django-rest-framework.org/) - The definitive source of information.
    
* **DRF Tutorial:** [https://www.django-rest-framework.org/tutorial/](https://www.django-rest-framework.org/tutorial/) - Walkthrough of building an API with DRF.
    
* **DRF GitHub:** [https://github.com/encode/django-rest-framework](https://github.com/encode/django-rest-framework) - Source code, issues, and discussions.
    

**PostgreSQL:**

* **Official PostgreSQL Documentation:** [https://www.postgresql.org/docs/](https://www.postgresql.org/docs/) - In-depth documentation for all PostgreSQL features.
    
* **PostgreSQL Tutorial:** [https://www.postgresqltutorial.com/](https://www.postgresqltutorial.com/) - A great place to start learning PostgreSQL.
    
* **PostgreSQL GitHub:** [https://github.com/postgres/postgres](https://github.com/postgres/postgres) - Source code, issues, and discussions.
    

**OAuth2 and JWT:**

* **OAuth 2.0 Specification:** [https://oauth.net/2/](https://oauth.net/2/) - Official specification of the OAuth2 protocol.
    
* **JWT (JSON Web Tokens):** [https://jwt.io/introduction](https://jwt.io/introduction) - Introduction to JWTs.
    
* **Understanding OAuth 2.0:** [https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) - A helpful guide on OAuth.
    

**GitHub Actions:**

* **Official GitHub Actions Documentation:** [https://docs.github.com/en/actions](https://docs.github.com/en/actions) - Comprehensive documentation for GitHub Actions.
    
* **GitHub Actions Tutorials:** [https://github.com/actions/starter-workflows](https://github.com/actions/starter-workflows) - Collection of examples.
    

**Docker:**

* **Official Docker Documentation:** [https://docs.docker.com/](https://docs.docker.com/) - Documentation of Docker and its features.
    
* **Docker Compose Documentation:** [https://docs.docker.com/compose/](https://docs.docker.com/compose/) - Documentation of Docker Compose and its features.
    
* **Docker Tutorial:** [https://www.docker.com/101-tutorial/](https://www.docker.com/101-tutorial/) - A good starting point to learn docker.
    

**Caddy:**

* **Official Caddy Documentation:** [https://caddyserver.com/docs/](https://caddyserver.com/docs/) - Documentation for setting up Caddy.
    
* **Caddy GitHub:** [https://github.com/caddyserver/caddy](https://github.com/caddyserver/caddy) - Source code, issues, and discussions.
    

**General Web Development Concepts:**

* **RESTful API Design:** [https://restfulapi.net/](https://restfulapi.net/) - Guidelines on designing RESTful APIs.
    
* **Security Best Practices:** [https://owasp.org/www-project-top-ten/](https://owasp.org/www-project-top-ten/) - Top 10 web application security risks.
    
* **Deployment Strategies:** [https://martinfowler.com/bliki/BlueGreenDeployment.html](https://martinfowler.com/bliki/BlueGreenDeployment.html) - Information on various deployment strategies.
    

By utilizing these resources, you can gain a deeper understanding of each technology, which will allow you to build your project more effectively. Let me know if you would like me to elaborate on any of the resources!
