diff --git a/Dockerfile b/Dockerfile index ebd59cb..4ff9e57 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,9 +8,9 @@ FROM node:22-alpine AS builder WORKDIR /app # نصب وابستگی‌ها با کش npm و وابسته به package-lock -# نکته: npm ci درست است؛ اگر fail شد یعنی package-lock.json باید sync شود. +# نکته: npm install درست است؛ اگر fail شد یعنی package-lock.json باید sync شود. COPY package.json package-lock.json ./ -RUN --mount=type=cache,target=/root/.npm npm ci +RUN --mount=type=cache,target=/root/.npm npm install # کپی سورس و build COPY . . diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..0638ea9 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,21 @@ +pipeline { + environment { + production_server_ip = "88.99.212.243" + production_server_name = "newhorizon_germany_001_server" + project_path = "/projects/aqila/aqila_panel" + } + agent any + stages { + stage("deploy"){ + steps{ + script{ + withCredentials([usernamePassword(credentialsId: production_server_name, usernameVariable: "USERNAME", passwordVariable: "PASSWORD")]) { + sh "sshpass -p $PASSWORD ssh -p 1782 $USERNAME@$production_server_ip -o StrictHostKeyChecking=no \"cd $project_path && ./runner.sh\"" + def lastCommit = sh(script: "git log -1 --pretty=format:\"%h - %s (%an)\"", returnStdout: true).trim() + sh "curl -F chat_id=-1002316394394 -F message_thread_id=11 -F document=@/var/jenkins_home/jobs/${env.JOB_NAME}/builds/${env.BUILD_NUMBER}/log -F caption='Project name: #${env.JOB_NAME} \nBuild status is ${currentBuild.currentResult} \nBuild url: ${BUILD_URL} \nLast Commit: ${lastCommit}' https://api.telegram.org/bot7207581748:AAFeymryw7S44D86LYfWqYK-tSNeV3TOwBs/sendDocument" + } + } + } + } + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 68bc8c0..a39fd81 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,39 +1,27 @@ -# docker-compose برای داشبورد حسینیه (SPA پشت nginx). -# -# منبع واحد env در پروداکشن: فایل .env.production (با env_file خوانده می‌شود). -# همهٔ متغیرها از همان یک جا می‌آیند و در runtime به کانتینر تزریق می‌شوند. - services: web: build: context: . dockerfile: Dockerfile - image: hosseinieh-dashboard:latest - container_name: hosseinieh-dashboard + image: aqila-panel:latest + container_name: aqila_panel restart: unless-stopped - # ← تنها منبع متغیرهای محیط در پروداکشن env_file: - .env.production - # ← پورت 5010 میزبان به 80 کانتینر (nginx) ports: - - "5010:80" - - # دسترسی به بک‌اندی که روی خود میزبان اجرا می‌شود (host.docker.internal) -# extra_hosts: -# - "host.docker.internal:host-gateway" + - "5013:80" healthcheck: - test: ["CMD", "wget", "-qO-", "http://localhost/healthz"] + test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz"] interval: 30s timeout: 3s retries: 3 start_period: 10s networks: - - najm_najm + - aqila_backend_aqila networks: - najm_najm: + aqila_backend_aqila: external: true - diff --git a/docker/40-config-js.sh b/docker/40-config-js.sh old mode 100644 new mode 100755 index 996ffd5..482f2f1 --- a/docker/40-config-js.sh +++ b/docker/40-config-js.sh @@ -1,26 +1,19 @@ #!/bin/sh -# تولید /config.js از متغیرهای محیط — «منبع واحدِ env در runtime». -# -# nginx رسمی هر اسکریپت اجراییِ /docker-entrypoint.d/*.sh را پیش از استارت سرور -# اجرا می‌کند. این‌جا مقادیر env (که از .env.production → docker-compose می‌آیند) -# را در window.__ENV__ می‌نویسیم تا app آن‌ها را در runtime بخواند — بدون rebuild. -# -# توجه: کلید Soniox از این مسیر نمی‌آید — در کد هاردکد شده است (نه env، نه API). set -eu TARGET="/usr/share/nginx/html/config.js" -# پیش‌فرض‌ها (هم‌راستا با fallbackهای کد). با env قابل override. VITE_API_BASE_URL="${VITE_API_BASE_URL:-/api}" +VITE_AUTH_SCHEME="${VITE_AUTH_SCHEME:-Token}" -# escape امنِ مقادیر برای قرارگیری داخل رشتهٔ JS (backslash، کوتیشن، خط جدید). js_escape() { - printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e "s/'/\\\\'/g" | tr '\n' ' ' + printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e "s/'/\\'/g" | tr '\n' ' ' } { echo "window.__ENV__ = {" echo " VITE_API_BASE_URL: '$(js_escape "$VITE_API_BASE_URL")'," + echo " VITE_AUTH_SCHEME: '$(js_escape "$VITE_AUTH_SCHEME")'," echo "};" } > "$TARGET" diff --git a/docker/nginx/default.conf.template b/docker/nginx/default.conf.template index 314a751..dbca8ff 100644 --- a/docker/nginx/default.conf.template +++ b/docker/nginx/default.conf.template @@ -25,27 +25,15 @@ server { } # ── پراکسیِ API → بک‌اند (${BACKEND_UPSTREAM}) ── - # اسلش انتهاییِ proxy_pass پیشوند «/api» را حذف می‌کند تا به ریشهٔ بک‌اند برسد - # (معادل rewrite: p.replace(/^\/api/, '') در vite.config.ts برای dev). - # - # چون به یک هاست خارجیِ HTTPS پراکسی می‌کنیم: - # • Host را برابر خودِ بک‌اند می‌گذاریم (نه $host=localhost:5010) چون Django - # روی ALLOWED_HOSTS/CORS بر اساس Host تصمیم می‌گیرد. - # • SNI را با proxy_ssl_server_name/proxy_ssl_name روشن می‌کنیم تا TLS - # handshake با نام درست انجام شود. location /api/ { - proxy_pass https://${BACKEND_UPSTREAM}/; + proxy_pass http://${BACKEND_UPSTREAM}; proxy_http_version 1.1; - proxy_set_header Host ${BACKEND_UPSTREAM}; + proxy_set_header Host panel.zainabiyatours.com; 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; - # TLS به سمت بک‌اند خارجی - proxy_ssl_server_name on; - proxy_ssl_name ${BACKEND_UPSTREAM}; - - # پشتیبانی از WebSocket/upgrade (برای آینده، مثلاً استریم) + # پشتیبانی از WebSocket/upgrade proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 300s; diff --git a/runner.sh b/runner.sh old mode 100644 new mode 100755 index b94662b..fa08e71 --- a/runner.sh +++ b/runner.sh @@ -1,2 +1,4 @@ - -git pull origin master && docker compose up -d --build +#!/bin/bash +git pull origin master +DOCKER_BUILDKIT=1 docker compose up -d --build web +docker exec nginx-nginx-1 nginx -s reload diff --git a/src/services/http.ts b/src/services/http.ts index d955338..669d8ff 100644 --- a/src/services/http.ts +++ b/src/services/http.ts @@ -117,7 +117,7 @@ export async function apiFetch(path: string, options: ApiFetchOptio if (!skipAuth) { const token = useAuthStore.getState().token - const authScheme = getEnv('VITE_AUTH_SCHEME') ?? 'Bearer' + const authScheme = getEnv('VITE_AUTH_SCHEME') ?? 'Token' if (token) finalHeaders.set('Authorization', `${authScheme} ${token}`) }