diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index 838ebe9..f4d7d35 100644 Binary files a/backend/celerybeat-schedule and b/backend/celerybeat-schedule differ diff --git a/docker-compose.yml b/docker-compose.yml index db4c09f..615400e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -122,13 +122,23 @@ services: image: nginx:alpine ports: - "80:80" + - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./certbot/www:/var/www/certbot:ro + - ./certbot/conf:/etc/letsencrypt:ro depends_on: - backend - frontend restart: unless-stopped + # --- Certbot (Let's Encrypt) --- + certbot: + image: certbot/certbot + volumes: + - ./certbot/www:/var/www/certbot + - ./certbot/conf:/etc/letsencrypt + volumes: redis_data: upload_data: diff --git a/fixed-ssl.md b/fixed-ssl.md new file mode 100644 index 0000000..d5a5e96 --- /dev/null +++ b/fixed-ssl.md @@ -0,0 +1,273 @@ +سؤالك ممتاز—وده بالضبط الفرق بين **حل “يشتغل وخلاص”** وحل **Production حقيقي**. + +خليني أشرح لك الأول **ليه الطريقة الأولى (certbot --nginx) مش مناسبة** عندك، وبعدين أديك **الحل الصحيح الجاهز (Nginx داخل Docker + SSL + Auto renew)** copy/paste. + +--- + +# لماذا `certbot --nginx` مش مناسب عندك؟ + +أنت عندك: + +```text +Nginx شغال داخل Docker (saas-pdf-nginx-1) +``` + +لكن الأمر: + +```bash +certbot --nginx +``` + +بيفترض إن: + +```text +Nginx مثبت على السيرفر (host) +``` + +📌 النتيجة: + +* Certbot حاول يتحكم في Nginx الخارجي +* لكن البورت 80 مستخدم بالفعل بواسطة Docker +* حصل الخطأ: `Address already in use` + +--- + +# هل الحل اللي عملناه “مؤقت”؟ + +لا 👇 + +## عندنا 3 مستويات حلول: + +### 🥉 حل بدائي + +تشغل Certbot خارج Docker → كل مرة توقف Nginx +❌ غير عملي + +--- + +### 🥈 حل متوسط + +Certbot standalone + manual config +✔ شغال +❌ لكن renewal مش أوتوماتيك بسهولة + +--- + +### 🥇 الحل الاحترافي (اللي هنطبقه الآن) + +```text +Nginx + Certbot داخل Docker +``` + +✔ Auto renew +✔ scalable +✔ نفس طريقة الشركات الكبيرة + +--- + +# المشاكل لو ما عملتش الحل الصحيح + +لو كملت بالحل القديم: + +* ❌ SSL هينتهي كل 90 يوم +* ❌ الموقع هيقع فجأة +* ❌ Google هيقلل ترتيبك +* ❌ AdSense ممكن يرفضك +* ❌ صعب التوسعة لاحقًا + +--- + +# الآن الحل الاحترافي (جاهز للتطبيق) + +هنعمل 3 حاجات: + +```text +1. nginx config احترافي +2. docker-compose فيه SSL +3. auto renew +``` + +--- + +# 1️⃣ تعديل nginx config + +ادخل: + +```bash +cd ~/SaaS-PDF/nginx +nano default.conf +``` + +واستبدل كله بهذا: + +```nginx +server { + listen 80; + server_name dociva.io www.dociva.io; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name dociva.io www.dociva.io; + + ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem; + + location / { + proxy_pass http://frontend:5173; + } +} +``` + +--- + +# 2️⃣ تعديل docker-compose.yml + +افتح: + +```bash +nano docker-compose.yml +``` + +وابحث عن nginx وعدله: + +```yaml +nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx:/etc/nginx/conf.d + - ./certbot/www:/var/www/certbot + - ./certbot/conf:/etc/letsencrypt + depends_on: + - frontend +``` + +--- + +# 3️⃣ إضافة certbot container + +في نفس الملف أضف: + +```yaml +certbot: + image: certbot/certbot + volumes: + - ./certbot/www:/var/www/certbot + - ./certbot/conf:/etc/letsencrypt +``` + +--- + +# 4️⃣ تشغيل المشروع + +```bash +docker compose down +docker compose up -d +``` + +--- + +# 5️⃣ إصدار الشهادة (مرة واحدة) + +```bash +docker compose run --rm certbot certonly \ +--webroot \ +--webroot-path=/var/www/certbot \ +-d dociva.io -d www.dociva.io \ +--email your@email.com \ +--agree-tos --no-eff-email +``` + +--- + +# 6️⃣ إعادة تشغيل Nginx + +```bash +docker compose restart nginx +``` + +--- + +# 7️⃣ اختبار + +```text +https://dociva.io +``` + +🔥 المفروض يشتغل + +--- + +# 8️⃣ auto renew (مهم جداً) + +أضف cron job: + +```bash +crontab -e +``` + +وأضف: + +```bash +0 3 * * * docker compose run --rm certbot renew && docker compose restart nginx +``` + +--- + +# النتيجة النهائية + +✔ HTTPS شغال +✔ Auto renew +✔ Production ready +✔ مناسب لـ SEO + AdSense + +--- + +# خلاصة بسيطة + +| الطريقة | هل مناسبة؟ | +| ---------------- | ---------- | +| certbot --nginx | ❌ لا | +| standalone | ⚠️ مؤقت | +| Docker SSL setup | ✅ الأفضل | + +--- + +# رأيي كمستشار + +أنت حالياً وصلت لمستوى: + +```text +Junior → Mid-level DevOps 🚀 +``` + +ولو كملت بالشكل ده مشروعك فعلاً ممكن يتحول لمنتج حقيقي. + +--- + +# الخطوة التالية بعد SSL + +بعد ما نخلص: + +1. Google Search Console +2. Sitemap +3. SEO pages +4. Analytics + +--- + +لو حابب، الخطوة الجاية أعملك: + +👉 **إعداد SEO كامل يجلب أول 10,000 زيارة** +بطريقة عملية مش كلام نظري. diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 74d067b..a913cde 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -6,12 +6,35 @@ upstream frontend { server frontend:5173; } +# ── HTTP: ACME challenge + redirect to HTTPS ── server { listen 80; - server_name localhost 127.0.0.1 178.104.57.123 dociva.io www.dociva.io; + server_name dociva.io www.dociva.io; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +# ── HTTPS ── +server { + listen 443 ssl; + server_name dociva.io www.dociva.io; client_max_body_size 100M; + # SSL certificates (Let's Encrypt) + ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + # Security headers + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; @@ -33,7 +56,7 @@ server { proxy_connect_timeout 60s; } - # Frontend (Vite dev server in dev, static in prod) + # Frontend (Vite dev server) location / { proxy_pass http://frontend; proxy_set_header Host $host; diff --git a/scripts/init-letsencrypt.sh b/scripts/init-letsencrypt.sh new file mode 100644 index 0000000..1518b10 --- /dev/null +++ b/scripts/init-letsencrypt.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# ──────────────────────────────────────────────────────────── +# init-letsencrypt.sh +# Bootstrap Let's Encrypt certificates for dociva.io +# +# This script solves the chicken-and-egg problem: +# Nginx needs SSL certs to start on 443, +# but Certbot needs Nginx running on 80 to verify the domain. +# +# Solution: create a temporary self-signed cert → start Nginx → +# obtain the real cert → reload Nginx. +# +# Usage: chmod +x scripts/init-letsencrypt.sh +# sudo ./scripts/init-letsencrypt.sh +# ──────────────────────────────────────────────────────────── + +set -euo pipefail + +DOMAINS=(dociva.io www.dociva.io) +DATA_PATH="./certbot" +EMAIL="admin@dociva.io" # ← replace with your real email +RSA_KEY_SIZE=4096 +STAGING=0 # Set to 1 to test against staging (no rate limits) + +# ── colour helpers ── +info() { echo -e "\n\033[1;34m▶ $*\033[0m"; } +ok() { echo -e "\033[1;32m✔ $*\033[0m"; } +fail() { echo -e "\033[1;31m✖ $*\033[0m"; exit 1; } + +# ── Pre-flight checks ── +command -v docker > /dev/null 2>&1 || fail "docker is not installed" +command -v docker compose > /dev/null 2>&1 && COMPOSE="docker compose" || COMPOSE="docker-compose" + +# ── Step 1: Create required directories ── +info "Creating certbot directories …" +mkdir -p "$DATA_PATH/conf" "$DATA_PATH/www" +ok "Directories ready" + +# ── Step 2: Download recommended TLS parameters ── +if [ ! -e "$DATA_PATH/conf/options-ssl-nginx.conf" ]; then + info "Downloading recommended TLS parameters …" + curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf \ + -o "$DATA_PATH/conf/options-ssl-nginx.conf" + curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem \ + -o "$DATA_PATH/conf/ssl-dhparams.pem" + ok "TLS parameters saved" +fi + +# ── Step 3: Create temporary self-signed certificate ── +LIVE_DIR="$DATA_PATH/conf/live/dociva.io" +if [ ! -e "$LIVE_DIR/fullchain.pem" ]; then + info "Creating temporary self-signed certificate …" + mkdir -p "$LIVE_DIR" + openssl req -x509 -nodes -newkey rsa:2048 -days 1 \ + -keyout "$LIVE_DIR/privkey.pem" \ + -out "$LIVE_DIR/fullchain.pem" \ + -subj "/CN=dociva.io" 2>/dev/null + ok "Temporary certificate created" +fi + +# ── Step 4: Start Nginx (uses the temp cert) ── +info "Starting Nginx …" +$COMPOSE up -d nginx +ok "Nginx is running" + +# ── Step 5: Remove the temporary certificate ── +info "Removing temporary certificate …" +rm -rf "$LIVE_DIR" +ok "Temporary certificate removed" + +# ── Step 6: Request real certificate from Let's Encrypt ── +info "Requesting Let's Encrypt certificate …" + +DOMAIN_ARGS="" +for d in "${DOMAINS[@]}"; do + DOMAIN_ARGS="$DOMAIN_ARGS -d $d" +done + +STAGING_ARG="" +if [ "$STAGING" -eq 1 ]; then + STAGING_ARG="--staging" +fi + +$COMPOSE run --rm certbot certonly --webroot \ + --webroot-path=/var/www/certbot \ + $DOMAIN_ARGS \ + --email "$EMAIL" \ + --agree-tos \ + --no-eff-email \ + --force-renewal \ + $STAGING_ARG + +ok "Certificate obtained successfully" + +# ── Step 7: Reload Nginx with the real certificate ── +info "Reloading Nginx …" +$COMPOSE exec nginx nginx -s reload +ok "Nginx reloaded with Let's Encrypt certificate" + +echo "" +ok "HTTPS is now active for ${DOMAINS[*]}" +echo " Test: curl -I https://dociva.io"