diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ab0eb81 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +venv +__pycache__ +*.pyc +*.pyo +*.pyd +*.sqlite3 +db.sqlite3 +.pytest_cache +.mypy_cache +.cache +.venv +.DS_Store +.git +*.egg-info +dist +build diff --git a/.env b/.env new file mode 100644 index 0000000..50a1fa2 --- /dev/null +++ b/.env @@ -0,0 +1,12 @@ +# Local Development Environment +DEBUG=True +SECRET_KEY=django-insecure-r616b$_bknw-2nh96gbuwe%2l-2o@g3uui747t8jz&nwp&xe_m +ALLOWED_HOSTS=localhost,127.0.0.1 + +# Database Configuration (Postgres) +DB_ENGINE=postgresql +DB_NAME=portfolio_admin +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0871b7e --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Django Settings +DEBUG=False +SECRET_KEY=your-secret-key-here-change-in-production +ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com + +# Database Configuration (Postgres) +DB_ENGINE=postgresql +DB_NAME=portfolio_admin +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1cc080e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv/ +products/ \ No newline at end of file diff --git a/content/__pycache__/admin.cpython-313.pyc b/content/__pycache__/admin.cpython-313.pyc index 5ef3b07..971d0e0 100644 Binary files a/content/__pycache__/admin.cpython-313.pyc and b/content/__pycache__/admin.cpython-313.pyc differ diff --git a/content/admin.py b/content/admin.py index 4a6ca8f..fb4b459 100644 --- a/content/admin.py +++ b/content/admin.py @@ -18,4 +18,4 @@ class ProductAdmin(admin.ModelAdmin): @admin.register(ContactMessage) class ContactAdmin(admin.ModelAdmin): list_display = ('name', 'phone', 'product_name', 'created_at') - search_fields = ('name', 'phone', 'product_name') + search_fields = ('name', 'phone', 'product_name') \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index d8a32c9..0000000 Binary files a/db.sqlite3 and /dev/null differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..89dd80b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +networks: + portfolio: + driver: bridge + +volumes: + pg_date: null + + +services: + nginx: + networks: + - portfolio + ports: + - ${PORT-8001}:80 + volumes: + - ./resources/layout/nginx.conf:/etc/nginx/nginx.conf + - ./resources:/usr/share/nginx/html/resources/ + build: + context: . + dockerfile: ./docker/Dockerfile.nginx + depends_on: + - web + + web: + networks: + - portfolio + build: + context: . + dockerfile: ./docker/Dockerfile.web + restart: always + command: sh resources/scripts/entrypoint.sh + environment: + - PYTHONPYCACHEPREFIX=/var/cache/pycache + + volumes: + - './:/code' + depends_on: + - db + + db: + image: postgres:16 + networks: + - portfolio + restart: always + environment: + POSTGRES_DB: ${DB_NAME} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_HOST: ${DB_HOST} + POSTGRES_PORT: ${DB_PORT} + volumes: + - pg_date:/var/lib/postgresql/data \ No newline at end of file diff --git a/docker/Dockerfile.nginx b/docker/Dockerfile.nginx new file mode 100644 index 0000000..42738e3 --- /dev/null +++ b/docker/Dockerfile.nginx @@ -0,0 +1,3 @@ +FROM nginx:alpine + +COPY ./resources/layout/nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/docker/Dockerfile.web b/docker/Dockerfile.web new file mode 100644 index 0000000..5472b77 --- /dev/null +++ b/docker/Dockerfile.web @@ -0,0 +1,13 @@ +FROM python:3.12-alpine + +ENV PYTHONPYCACHEPREFIX=/dev/null + +RUN apk update && apk add git gettext + +WORKDIR /code + +COPY requirements.txt /code/requirements.txt + +RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt + +CMD ["sh", "./entrypoint.sh"] \ No newline at end of file diff --git a/portfolio_admin/settings.py b/portfolio_admin/settings.py index 9f8c871..01ba429 100644 --- a/portfolio_admin/settings.py +++ b/portfolio_admin/settings.py @@ -10,7 +10,12 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.2/ref/settings/ """ +import os from pathlib import Path +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -20,12 +25,12 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-r616b$_bknw-2nh96gbuwe%2l-2o@g3uui747t8jz&nwp&xe_m' +SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-r616b$_bknw-2nh96gbuwe%2l-2o@g3uui747t8jz&nwp&xe_m') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') # Application definition @@ -75,12 +80,26 @@ WSGI_APPLICATION = 'portfolio_admin.wsgi.application' # Database # https://docs.djangoproject.com/en/5.2/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', +DB_ENGINE = os.getenv('DB_ENGINE', 'sqlite3') + +if DB_ENGINE == 'postgresql': + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('DB_NAME', 'portfolio_admin'), + 'USER': os.getenv('DB_USER', 'postgres'), + 'PASSWORD': os.getenv('DB_PASSWORD', 'postgres'), + 'HOST': os.getenv('DB_HOST', 'db'), + 'PORT': os.getenv('DB_PORT', '5432'), + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } } -} # Password validation diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..19acd47 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +asgiref==3.11.0 +Django==5.2.8 +djangorestframework==3.16.1 +pillow==12.0.0 +sqlparse==0.5.4 +gunicorn +psycopg2-binary==2.9.9 +python-dotenv==1.0.0 diff --git a/resources/layout/nginx.conf b/resources/layout/nginx.conf new file mode 100644 index 0000000..e10373b --- /dev/null +++ b/resources/layout/nginx.conf @@ -0,0 +1,53 @@ +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 1024M; + + + server { + listen 80; + server_name _; + + location / { + proxy_pass http://web:8000; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header Host $http_host; + } + + location /ws/ { + proxy_pass http://web:8000; # Uvicorn serveri ishga tushadigan port + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $http_host; + } + + location /resources/static/ { + alias /usr/share/nginx/html/resources/static/; + } + + location /resources/media/ { + alias /usr/share/nginx/html/resources/media/; + } + } +} \ No newline at end of file diff --git a/resources/scripts/entrypoint.sh b/resources/scripts/entrypoint.sh new file mode 100644 index 0000000..f91f22f --- /dev/null +++ b/resources/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +python3 manage.py collectstatic --noinput +python3 manage.py makemigrations --noinput +python3 manage.py migrate --noinput + +uvicorn config.asgi:application --host 0.0.0.0 --port 8000 --reload --reload-dir content --reload-dir portfolio_admin + +exit $? \ No newline at end of file