feat(deploy): add qlockify.ir domain and ssl config

This commit is contained in:
2026-04-29 17:27:49 +03:30
parent 596e2716ab
commit 34af725f41
7 changed files with 144 additions and 70 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ backend/.pytest_cache/
backend/qlockify-backend-deployment backend/qlockify-backend-deployment
frontend/qlockify-frontend-deployment frontend/qlockify-frontend-deployment
nginx/certs/*
!nginx/certs/.gitkeep

View File

@@ -2,7 +2,10 @@
This repository is the deployment layer only. This repository is the deployment layer only.
Docker builds read from the local `./backend` and `./frontend` directories inside this repository. Docker builds now read from nested application source directories inside this repository:
- `./backend/qlockify-backend-deployment`
- `./frontend/qlockify-frontend-deployment`
Those directories are expected to contain the backend and frontend application source before you build for deployment. Those directories are expected to contain the backend and frontend application source before you build for deployment.
## Local structure ## Local structure
@@ -12,7 +15,9 @@ The expected deployment layout is:
```text ```text
qlockify-deployment/ qlockify-deployment/
backend/ backend/
qlockify-backend-deployment/
frontend/ frontend/
qlockify-frontend-deployment/
nginx/ nginx/
postgres/ postgres/
docker-compose.yml docker-compose.yml
@@ -21,12 +26,12 @@ qlockify-deployment/
## Deployment flow ## Deployment flow
1. Put your application source into: 1. Put your application source into:
- `./backend` - `./backend/qlockify-backend-deployment`
- `./frontend` - `./frontend/qlockify-frontend-deployment`
2. Configure deployment env files: 2. Configure deployment env files:
- `./.env` - `./.env`
- `./backend/.env` - `./backend/qlockify-backend-deployment/.env`
- `./frontend/.env` - `./frontend/qlockify-frontend-deployment/.env`
3. From `qlockify-deployment`, build and start the stack: 3. From `qlockify-deployment`, build and start the stack:
```powershell ```powershell
@@ -35,6 +40,42 @@ docker compose up --build
The backend container runs database migrations and `collectstatic` on startup, then serves Django with Gunicorn using `config.wsgi:application`. The backend container runs database migrations and `collectstatic` on startup, then serves Django with Gunicorn using `config.wsgi:application`.
## Domain setup
The Nginx config is prepared for:
- `qlockify.ir`
- `www.qlockify.ir`
Requests to `www.qlockify.ir` are redirected to `qlockify.ir`.
Before bringing the stack up in production:
1. Point the DNS `A` or `AAAA` records for `qlockify.ir` and `www.qlockify.ir` to your server.
2. Set the backend env values in `./backend/qlockify-backend-deployment/.env`:
- `DJANGO_ALLOWED_HOSTS=qlockify.ir,www.qlockify.ir`
- `CORS_ALLOWED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir`
- `CSRF_TRUSTED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir`
- `BASE_URL=https://qlockify.ir`
3. Set the frontend env value in `./frontend/qlockify-frontend-deployment/.env`:
- `VITE_API_BASE_URL=https://qlockify.ir/api`
## SSL certificates
HTTPS is configured through Nginx if you place these files in:
```text
./nginx/certs/fullchain.pem
./nginx/certs/privkey.pem
```
The repo keeps `./nginx/certs/.gitkeep` only. Actual certificate files are ignored by git.
With the current Nginx config:
- `http://qlockify.ir` redirects to `https://qlockify.ir`
- `http://www.qlockify.ir` redirects to `https://qlockify.ir`
- `https://www.qlockify.ir` redirects to `https://qlockify.ir`
Make sure port `443` is open on the server firewall before starting the stack.
## SSE Notifications ## SSE Notifications
Notifications now use Server-Sent Events at `/api/notifications/stream/`. Notifications now use Server-Sent Events at `/api/notifications/stream/`.

View File

@@ -5,7 +5,7 @@ DEBUG=True
# Django Core # Django Core
DJANGO_SETTINGS_MODULE=config.settings DJANGO_SETTINGS_MODULE=config.settings
DJANGO_SECRET_KEY= DJANGO_SECRET_KEY=
DJANGO_ALLOWED_HOSTS= DJANGO_ALLOWED_HOSTS=qlockify.ir,www.qlockify.ir
# Database # Database
POSTGRES_DB=app_db POSTGRES_DB=app_db
@@ -15,8 +15,8 @@ POSTGRES_HOST=db
POSTGRES_PORT=5432 POSTGRES_PORT=5432
# CORS / CSRF # CORS / CSRF
CORS_ALLOWED_ORIGINS=https://app.example.com CORS_ALLOWED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir
CSRF_TRUSTED_ORIGINS=https://app.example.com CSRF_TRUSTED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir
# JWT # JWT
ACCESS_TOKEN_LIFETIME=5 ACCESS_TOKEN_LIFETIME=5
@@ -41,4 +41,4 @@ LANGUAGE_CODE=en-us
TIME_ZONE=Asia/Tehran TIME_ZONE=Asia/Tehran
SMS_APIKEY= SMS_APIKEY=
BASE_URL= BASE_URL=https://qlockify.ir

View File

@@ -77,9 +77,10 @@ services:
restart: always restart: always
ports: ports:
- "80:80" - "80:80"
# - "443:443" # Uncomment when adding SSL - "443:443"
volumes: volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
- ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro - ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro
- static_data:/usr/share/nginx/html/staticfiles:ro - static_data:/usr/share/nginx/html/staticfiles:ro
- media_data:/usr/share/nginx/html/mediafiles:ro - media_data:/usr/share/nginx/html/mediafiles:ro

View File

@@ -1 +1 @@
VITE_API_BASE_URL=http://localhost/api VITE_API_BASE_URL=https://qlockify.ir/api

1
nginx/certs/.gitkeep Normal file
View File

@@ -0,0 +1 @@

View File

@@ -1,6 +1,36 @@
server { server {
listen 80; listen 80;
server_name localhost; server_name qlockify.ir www.qlockify.ir;
return 301 https://qlockify.ir$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name www.qlockify.ir;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
return 301 https://qlockify.ir$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name qlockify.ir;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
client_max_body_size 100M; client_max_body_size 100M;
sendfile on; sendfile on;
@@ -18,7 +48,7 @@ server {
access_log off; access_log off;
} }
# Protect API Documentation with Basic Auth (from your old project) # Protect API Documentation with Basic Auth
location ~ ^/(docs|redoc|openapi.json|api/docs|api/redoc|api/openapi.json|api/v1/docs) { location ~ ^/(docs|redoc|openapi.json|api/docs|api/redoc|api/openapi.json|api/v1/docs) {
auth_basic "Restricted API Documentation"; auth_basic "Restricted API Documentation";
auth_basic_user_file /etc/nginx/.htpasswd; auth_basic_user_file /etc/nginx/.htpasswd;
@@ -30,7 +60,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Standard API Proxy
location /api/notifications/stream/ { location /api/notifications/stream/ {
proxy_pass http://backend:8000; proxy_pass http://backend:8000;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -54,7 +83,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Admin Panel Proxy
location /admin/ { location /admin/ {
proxy_pass http://backend:8000; proxy_pass http://backend:8000;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -63,10 +91,11 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Frontend Proxy
location / { location / {
proxy_pass http://frontend:80; proxy_pass http://frontend:80;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; 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;
} }
} }