Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
meminciftci committed Apr 28, 2024
2 parents 30911e3 + 4de27f4 commit 928e24e
Show file tree
Hide file tree
Showing 47 changed files with 3,462 additions and 145 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Continuous Integration

on:
push:
branches:
- main

concurrency:
group: main
cancel-in-progress: true

jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Configure SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/ci_cd_zenith
chmod 600 ~/.ssh/ci_cd_zenith
cat >>~/.ssh/config <<END
Host target
HostName $SSH_HOST
User $SSH_USER
IdentityFile ~/.ssh/ci_cd_zenith
LogLevel ERROR
StrictHostKeyChecking no
END
- name: Run deploy
run: |
ssh target "cd bounswe2024group11/backend/ && docker-compose down && git pull && docker-compose build && docker-compose up -d --force-recreate"
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome"
"editor.defaultFormatter": "biomejs.biome",
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
5 changes: 5 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DB_NAME=<database_name>
MYSQL_USER=<mysql_username>
MYSQL_PASSWORD=<mysql_password>
DB_HOST=<IP_address>
DB_PORT=<port_number>
4 changes: 3 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,6 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.idea/

test.rest
29 changes: 29 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# base image
FROM python:3.12-slim-bullseye
# setup environment variable
ENV DockerHOME=/home/app/backend

# set work directory
RUN mkdir -p $DockerHOME

# where your code lives
WORKDIR $DockerHOME

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install dependencies for mysqlclient
RUN apt-get update && apt-get install -y pkg-config python3-dev default-libmysqlclient-dev build-essential

# install dependencies
RUN pip install --upgrade pip

# copy whole project to your docker home directory.
COPY . $DockerHOME
# run this command to install all dependencies
RUN pip install --no-cache-dir -r requirements.txt
# port where the Django app runs
EXPOSE 8000
# start server
CMD python manage.py runserver
117 changes: 117 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Backend Development

See the [Full Version](https://github.com/bounswe/bounswe2024group11/wiki/How-to-Prepare-Your-Local-PC-for-the-Backend-Development/_edit) on Wiki

## How to Run with Docker

### Prerequisites

- Docker

### Steps

- 1. Clone the repository

```bash
git clone
```

- 2. Change directory to the project's backend directory

```bash
cd ./bounswe2024group11/backend
```

- 3. Create a `.env` file in the `./backend` directory and add the following environment variables

```bash
cp .env.example .env
```

- 4. Enter the values for the environment variables in the `.env` file.

You can take the credentials for the deployment from the Whatsapp group.
Or you can use local credentials for development purposes.

- 4.b (Optional) If you want to use local credentials, migrate the database

Run your MySQL server.

Then run the following commands to migrate the database

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

- 5. Install Docker and Docker Compose

- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose](https://docs.docker.com/compose/install/)

- 6. Run the following command to start the backend server

```bash
docker-compose up
```

## How to Run Manually

We need to create a virtual environment and do all backend-related jobs inside that virtual environment.

```bash
# Change directory to the project's backend directory
cd ./bounswe2024group11/backend

# create a virtual environment

## On bash/zsh shells (i.e., on macOS and the most of the Linux-based distros)
python3 -m venv venv

## On Windows
python -m venv venv

# Activate the virtual environment

## On bash/zsh shells (i.e., on macOS and the most of the Linux-based distros)
source ./venv/bin/activate

## On Windows
venv\Scripts\Activate.ps1 (Powershell)
venv\Scripts\activate.bat (cmd)
```

While initializing our project, we decided to use Python 3.12. So create your virtual environments accordingly.
You may want to take a look at [Python Documentation](https://docs.python.org/3/library/venv.html).

### Installations

Before starting development, we need to install all the requirements. Inside the `./backend` directory:

```bash
# Install requirements
pip install -r requirements.txt
```

Alternatively, you can install them manually. However, **the list of required libraries may and probably will change** from time to time. So, at the very beginning of the project, you can use these commands, but be careful when you want to use them later.

```bash
# Install Django
pip install Django

# Install Django REST Framework
pip install djangorestframework
```

### Run the app

To check if everything is okay, try to run the Django project. Inside the `./backend` directory:

```bash
# Run the Django project server
python manage.py runserver
```

If you see a page without an error when you visit the URL in the terminal, which is similar to `http://127.0.0.1:8000`, your server works properly.

Congratulations! You have cloned the project, created a virtual environment, and installed the dependencies. Now, you are ready to start your development.
Empty file added backend/api/__init__.py
Empty file.
7 changes: 7 additions & 0 deletions backend/api/serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from rest_framework import serializers
from user.models import User

class UserSerializer(serializers.ModelSerializer):
class Meta(object):
model = User
fields = '__all__'
Empty file added backend/api/tests/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions backend/api/tests/test_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from rest_framework.test import APITestCase
from django.urls import reverse
from faker import Faker

class TestSetUp(APITestCase):
def setUp(self):
self.register_url = reverse('signup')
self.login_url = reverse('login')


self.user_data1 = {
'username': Faker().user_name(),
'email': Faker().email(),
'password': Faker().password(),
}

self.user_data2 = {
'username': Faker().user_name(),
'email': Faker().email(),
'password': Faker().password(),
}

return super().setUp()

def tearDown(self):
return super().tearDown()
38 changes: 38 additions & 0 deletions backend/api/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from .test_setup import TestSetUp
from django.urls import reverse
from faker import Faker

class TestView(TestSetUp):

def test_register_user(self):
response = self.client.post(self.register_url, self.user_data1, format='json')
print(response.data)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['user']['username'], self.user_data1['username'])
self.assertEqual(response.data['user']['email'], self.user_data1['email'])
self.assertTrue('token' in response.data)

def test_register_nonunique_username(self):
response = self.client.post(self.register_url, self.user_data1, format='json')
response = self.client.post(self.register_url, self.user_data1, format='json')
print(response.data)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.data['username'][0], 'user with this username already exists.')

def test_false_login_user(self):
response = self.client.post(self.login_url, self.user_data1, format='json')
print(response.data)
self.assertEqual(response.status_code, 401)

def test_login_user(self):
self.client.post(self.register_url, self.user_data1, format='json')
response = self.client.post(self.login_url, self.user_data1, format='json')
print(response.data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['user']['username'], self.user_data1['username'])
self.assertEqual(response.data['user']['email'], self.user_data1['email'])
self.assertTrue('token' in response.data)




9 changes: 9 additions & 0 deletions backend/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.urls import path
from . import views

urlpatterns = [
# path('test_token', views.test_token, name="test_token"),
path('signup', views.register, name="signup"),
path('login', views.login, name="login"),

]
45 changes: 45 additions & 0 deletions backend/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from rest_framework.response import Response
from rest_framework.decorators import api_view
from user.models import User
from .serializer import UserSerializer
from rest_framework import status
from rest_framework.authtoken.models import Token
from django.shortcuts import get_object_or_404

@api_view(['POST'])
def register(request):
required_fields = ['username', 'password', 'email']
if not all([field in request.data for field in required_fields]):
return Response({"res":"Please provide all required fields."}, status=status.HTTP_400_BAD_REQUEST)

serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
user = User.objects.get(username=request.data['username'])
user.set_password(request.data['password'])
user.save()
token = Token.objects.create(user=user)
return Response({"token": token.key, "user": serializer.data}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def login(request):
required_fields = ['username', 'password']
if not all([field in request.data for field in required_fields]):
return Response({"res":"Please provide both username and password."}, status=status.HTTP_400_BAD_REQUEST)

try:
user = User.objects.get(username=request.data['username'])
except User.DoesNotExist:
return Response({"res": "Username not found."}, status=status.HTTP_401_UNAUTHORIZED)

if not user.check_password(request.data['password']):
return Response({"res":"password does not match."}, status=status.HTTP_401_UNAUTHORIZED)
token, created = Token.objects.get_or_create(user=user)
serializer = UserSerializer(instance=user)
return Response({"token": token.key, "user": serializer.data}, status=status.HTTP_200_OK)


#@api_view(['GET'])
#def test_token(request):
# return Response({"res":"Token is valid!"})
19 changes: 16 additions & 3 deletions backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"""

from pathlib import Path
from dotenv import load_dotenv
import os

load_dotenv('./.env')

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand All @@ -25,7 +29,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['159.65.125.158', '164.90.189.150', 'localhost', '127.0.0.1']


# Application definition
Expand All @@ -38,8 +42,12 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'user',
]

AUTH_USER_MODEL = 'user.User'

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
Expand Down Expand Up @@ -76,12 +84,17 @@

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'ENGINE': 'django.db.backends.mysql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('MYSQL_USER'),
'PASSWORD': os.getenv('MYSQL_PASSWORD'),
'HOST': os.getenv('DB_HOST'), # Or your MySQL server's IP address
'PORT': os.getenv('DB_PORT'), # Default MySQL port
}
}



# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators

Expand Down
Loading

0 comments on commit 928e24e

Please sign in to comment.