From ed47645fef433a51b4e6772393241915b80d436c Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Sun, 15 Mar 2026 10:20:55 +0800 Subject: [PATCH] initial commit --- .env.sample | 3 ++ .gitignore | 3 ++ backend/.env.sample | 44 +++++++++++++++++++++ backend/Dockerfile | 25 ++++++++++++ docker-compose.yml | 93 ++++++++++++++++++++++++++++++++++++++++++++ frontend/.env.sample | 1 + frontend/Dockerfile | 18 +++++++++ nginx/.htpasswd | 1 + nginx/nginx.conf | 55 ++++++++++++++++++++++++++ postgres/init.sql | 1 + postgres/pg_hba.conf | 11 ++++++ 11 files changed, 255 insertions(+) create mode 100644 .env.sample create mode 100644 .gitignore create mode 100644 backend/.env.sample create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 frontend/.env.sample create mode 100644 frontend/Dockerfile create mode 100644 nginx/.htpasswd create mode 100644 nginx/nginx.conf create mode 100644 postgres/init.sql create mode 100644 postgres/pg_hba.conf diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..aa38abd --- /dev/null +++ b/.env.sample @@ -0,0 +1,3 @@ +POSTGRES_DB=qlockify +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b17657e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +backend/qlockify-backend/ +frontend/qlockify-frontend/ diff --git a/backend/.env.sample b/backend/.env.sample new file mode 100644 index 0000000..45fa934 --- /dev/null +++ b/backend/.env.sample @@ -0,0 +1,44 @@ +# Environment +ENVIRONMENT=development +DEBUG=True + +# Django Core +DJANGO_SETTINGS_MODULE=config.settings +DJANGO_SECRET_KEY= +DJANGO_ALLOWED_HOSTS= + +# Database +POSTGRES_DB=app_db +POSTGRES_USER=app_user +POSTGRES_PASSWORD=app_password +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 + +# CORS / CSRF +CORS_ALLOWED_ORIGINS=https://app.example.com +CSRF_TRUSTED_ORIGINS=https://app.example.com + +# JWT +ACCESS_TOKEN_LIFETIME=5 +JWT_SECRET_KEY= +JWT_SIGNING_KEY= +JWT_ACCESS_TOKEN_LIFETIME_MINUTES=5 +JWT_REFRESH_TOKEN_LIFETIME_DAYS=7 +JWT_ROTATE_REFRESH_TOKENS=True +JWT_BLACKLIST_AFTER_ROTATION=True +JWT_ALGORITHM=HS256 + +# Redis / Celery +REDIS_URL=redis://redis:6379/0 +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_PASSWORD= +CELERY_BROKER_URL= +CELERY_RESULT_BACKEND= + +# Timzone / Language +LANGUAGE_CODE=en-us +TIME_ZONE=Asia/Tehran + +SMS_APIKEY= +BASE_URL= diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..93c95c4 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,25 @@ +FROM python:3.14-slim + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_INDEX_URL=https://package-mirror.liara.ir/repository/pypi/simple + +# Adapted Runflare mirror for Debian-based official Python image +RUN . /etc/os-release && \ + echo "deb http://mirror-linux.runflare.com/debian $VERSION_CODENAME main" > /etc/apt/sources.list && \ + echo "deb http://mirror-linux.runflare.com/debian $VERSION_CODENAME-updates main" >> /etc/apt/sources.list && \ + echo "deb http://mirror-linux.runflare.com/debian-security $VERSION_CODENAME-security main" >> /etc/apt/sources.list + +RUN apt-get update \ + && apt-get install -y gcc libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY qlockify-backend/requirements/ /app/requirements/ +RUN pip install --no-cache-dir -r requirements/base.txt \ + && pip install --no-cache-dir -r requirements/prod.txt + +COPY qlockify-backend/ . + +CMD ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4e63ff1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,93 @@ +version: '3.8' + +services: + db: + image: postgres:18-alpine + restart: always + env_file: + - .env + volumes: + - postgres_data:/var/lib/postgresql/data + - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql + - ./postgres/custom-postgresql.conf:/etc/postgresql/postgresql.conf:ro + - ./postgres/pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf:ro + command: postgres -c config_file=/etc/postgresql/postgresql.conf + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + ports: + - "127.0.0.1:5432:5432" # Bound to localhost for security (as in old project) + + redis: + image: redis:7-alpine + restart: always + ports: + - "127.0.0.1:6379:6379" + + backend: + build: + context: ./backend + dockerfile: Dockerfile + restart: always + env_file: + - ./backend/.env + volumes: + - static_data:/app/staticfiles + - media_data:/app/mediafiles + expose: + - "8000" + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + + celery: + build: + context: ./backend + dockerfile: Dockerfile + restart: always + env_file: + - ./backend/.env + volumes: + - media_data:/app/mediafiles + command: celery -A core worker -l INFO + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + backend: + condition: service_started + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + restart: always + env_file: + - ./frontend/.env + expose: + - "80" + + nginx: + image: nginx:alpine + restart: always + ports: + - "80:80" + # - "443:443" # Uncomment when adding SSL + volumes: + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro + - static_data:/usr/share/nginx/html/staticfiles:ro + - media_data:/usr/share/nginx/html/mediafiles:ro + depends_on: + - backend + - frontend + +volumes: + postgres_data: + static_data: + media_data: diff --git a/frontend/.env.sample b/frontend/.env.sample new file mode 100644 index 0000000..de4c9d5 --- /dev/null +++ b/frontend/.env.sample @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost/api diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..a29e6b4 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,18 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ --global + +COPY qlockify-frontend/package*.json ./ +RUN npm install + +COPY qlockify-frontend/ . +RUN npm run build + +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +# Internal Nginx configuration (Root Nginx acts as reverse proxy to this) +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx/.htpasswd b/nginx/.htpasswd new file mode 100644 index 0000000..6e61564 --- /dev/null +++ b/nginx/.htpasswd @@ -0,0 +1 @@ +admin:$2y$05$gSZ4s3BN8TsEc.pS/vaZi.v/AMrIozncWtFDGkNOglJlv59f7jc7i diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..b5103fc --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,55 @@ +server { + listen 80; + server_name localhost; + + client_max_body_size 100M; + sendfile on; + + # Static and Media files + location /static/ { + alias /usr/share/nginx/html/staticfiles/; + expires 30d; + access_log off; + } + + location /media/ { + alias /usr/share/nginx/html/mediafiles/; + expires 30d; + access_log off; + } + + # Protect API Documentation with Basic Auth (from your old project) + location ~ ^/(docs|redoc|openapi.json|api/docs|api/redoc|api/openapi.json|api/v1/docs) { + auth_basic "Restricted API Documentation"; + auth_basic_user_file /etc/nginx/.htpasswd; + + proxy_pass http://backend:8000; + proxy_set_header Host $host; + 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 $scheme; + } + + # Standard API Proxy + location /api/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # Admin Panel Proxy + location /admin/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # Frontend Proxy + location / { + proxy_pass http://frontend:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} diff --git a/postgres/init.sql b/postgres/init.sql new file mode 100644 index 0000000..7d9e4ae --- /dev/null +++ b/postgres/init.sql @@ -0,0 +1 @@ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; diff --git a/postgres/pg_hba.conf b/postgres/pg_hba.conf new file mode 100644 index 0000000..fead8b2 --- /dev/null +++ b/postgres/pg_hba.conf @@ -0,0 +1,11 @@ +# TYPE DATABASE USER ADDRESS METHOD +local all all scram-sha-256 +host all all 127.0.0.1/32 scram-sha-256 + +# Allow Docker containers to connect (Standard Docker bridge subnets) +host all all 172.16.0.0/12 scram-sha-256 +host all all 192.168.0.0/16 scram-sha-256 + +# Reject everything else +host all all 0.0.0.0/0 reject +host all all ::/0 reject