diff --git a/.gitea/workflows/be-deploy.yml b/.gitea/workflows/be-deploy.yml index fb5d489..5434c03 100644 --- a/.gitea/workflows/be-deploy.yml +++ b/.gitea/workflows/be-deploy.yml @@ -56,7 +56,7 @@ jobs: IMAGE_TAG="${{ inputs.image_tag }}" mkdir -p "${DEPLOY_DIR}" - cp be/docker-compose.yml "${DEPLOY_DIR}/docker-compose.yml" + cp be/prod-compose.yml "${DEPLOY_DIR}/docker-compose.yml" cd "${DEPLOY_DIR}" diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c4affc7..e79df98 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -64,9 +64,14 @@ jobs: IMAGE="git.koppkb.com/kopkb/qms-fe-admin" TAGS="-t ${IMAGE}:${{ gitea.sha }}" API_URL="${{ secrets.VITE_API_URL }}" + # Branch QR display URL only (public domain via nginx) + BRANCH_QR_BASE_URL="${{ secrets.VITE_BRANCH_QR_BASE_URL }}" + if [ -z "${BRANCH_QR_BASE_URL}" ]; then + BRANCH_QR_BASE_URL="https://qms-customer.erahn.com.my" + fi if [ -z "${API_URL}" ]; then - echo "ERROR: Set Gitea secret VITE_API_URL (e.g. https://qms-api.erahn.com.my)" + echo "ERROR: Set Gitea secret VITE_API_URL (e.g. http://172.16.6.200:8080)" exit 1 fi @@ -76,9 +81,11 @@ jobs: echo "Building and pushing admin: ${TAGS}" echo "VITE_API_URL=${API_URL}" + echo "VITE_BRANCH_QR_BASE_URL=${BRANCH_QR_BASE_URL}" docker buildx build \ --platform linux/amd64 \ --build-arg VITE_API_URL="${API_URL}" \ + --build-arg VITE_BRANCH_QR_BASE_URL="${BRANCH_QR_BASE_URL}" \ --cache-from type=registry,ref=${IMAGE}:buildcache,ignore-error=true \ --cache-to type=registry,ref=${IMAGE}:buildcache,mode=max \ -f fe/web/admin-app/Dockerfile \ @@ -109,7 +116,7 @@ jobs: API_URL="${{ secrets.VITE_API_URL }}" if [ -z "${API_URL}" ]; then - echo "ERROR: Set Gitea secret VITE_API_URL (e.g. https://qms-api.erahn.com.my)" + echo "ERROR: Set Gitea secret VITE_API_URL (e.g. http://172.16.6.200:8080)" exit 1 fi @@ -154,7 +161,7 @@ jobs: fi if [ -z "${API_URL}" ]; then - echo "ERROR: Set Gitea secret NEXT_PUBLIC_API_URL or VITE_API_URL (e.g. https://qms-api.erahn.com.my)" + echo "ERROR: Set Gitea secret NEXT_PUBLIC_API_URL or VITE_API_URL (e.g. http://172.16.6.200:8080)" exit 1 fi diff --git a/README.md b/README.md index 7d830d0..592502e 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,10 @@ The backend of the queue management system will be developed using Java's Spring - [Ahmed Ljubuncic](https://github.com/aljubuncic) - [Vedran Mujic](https://github.com/vmujic1) - [Amar Tahirovic](https://github.com/amarderschrecklicher) + +## API ENV GUIDE +VITE_API_URL: API for teller and admin (also customer fallback in CI) +NEXT_PUBLIC_API_URL: API for customer +VITE_BRANCH_QR_BASE_URL: public customer base URL encoded into admin branch QR + +Match these in Gitea secrets and in `fe/web/.env` on the deploy host (ports/images only matter at deploy; API URLs matter at image build). \ No newline at end of file diff --git a/be/prod-compose.yml b/be/prod-compose.yml new file mode 100644 index 0000000..d56a505 --- /dev/null +++ b/be/prod-compose.yml @@ -0,0 +1,64 @@ +services: + mysql: + image: mysql:8 + container_name: qms-mysql + restart: unless-stopped + ports: + - "${MYSQL_HOST_PORT:-3307}:3306" + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password} + MYSQL_DATABASE: ${MYSQL_DATABASE:-qms} + volumes: + - qms-mysql-data:/var/lib/mysql + healthcheck: + test: + [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "-p${MYSQL_ROOT_PASSWORD:-password}", + ] + interval: 5s + timeout: 5s + retries: 20 + start_period: 20s + networks: + - qms-net + + backend: + image: ${BACKEND_IMAGE:-qms-backend}:${IMAGE_TAG:-local} + build: + context: . + dockerfile: Dockerfile + container_name: qms-backend + restart: unless-stopped + ports: + # Host port can be changed via BACKEND_HOST_PORT in .env (default 8080) + - "${BACKEND_HOST_PORT:-8080}:8080" + environment: + SERVER_PORT: 8080 + # Use Docker service name "mysql", not localhost + SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-qms}?allowPublicKeyRetrieval=true&useSSL=false + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password} + JWT_SECRET_KEY: ${JWT_SECRET_KEY:-a68uiaDQ0V3iLjF4DqMuS13GAVwkut55dlFbGCLyXTF} + ADS_UPLOAD_DIR: /app/uploads/ads + NOTIFICATIONS_MOCK: ${NOTIFICATIONS_MOCK:-true} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-dummy-google-client-id} + volumes: + - qms-uploads:/app/uploads + depends_on: + mysql: + condition: service_healthy + networks: + - qms-net + +volumes: + qms-mysql-data: + qms-uploads: + +networks: + qms-net: + driver: bridge diff --git a/fe/web/.env.domain b/fe/web/.env.domain index b086a63..05df3a8 100644 --- a/fe/web/.env.domain +++ b/fe/web/.env.domain @@ -1,25 +1,14 @@ -# Host ports (used until nginx terminates TLS on 443) +# Host ports (nginx terminates TLS on 443 and proxies to these) ADMIN_HOST_PORT=5000 TELLER_HOST_PORT=3001 CUSTOMER_HOST_PORT=3000 -# Domains — also set these as Gitea secrets, then rebuild FE images -# VITE_API_URL / NEXT_PUBLIC_API_URL = https://qms-api.erahn.com.my -# VITE_CUSTOMER_APP_URL = https://qms-customer.erahn.com.my VITE_API_URL=https://qms-api.erahn.com.my -VITE_CUSTOMER_APP_URL=https://qms-customer.erahn.com.my NEXT_PUBLIC_API_URL=https://qms-api.erahn.com.my +VITE_BRANCH_QR_BASE_URL=https://qms-customer.erahn.com.my -# Public app URLs (reference for nginx / bookmarks) -# ADMIN: https://qms-admin.erahn.com.my -# TELLER: https://qms-teller.erahn.com.my -# CUSTOMER: https://qms-customer.erahn.com.my -# API: https://qms-api.erahn.com.my - -# Runtime (customer Next.js server) QR_TOKEN_SECRET=qms-branch-qr-v1 -# Registry images (used by compose / deploy) ADMIN_IMAGE=git.koppkb.com/kopkb/qms-fe-admin TELLER_IMAGE=git.koppkb.com/kopkb/qms-fe-teller CUSTOMER_IMAGE=git.koppkb.com/kopkb/qms-fe-customer diff --git a/fe/web/.env.example b/fe/web/.env.example index 12c3183..283f8e6 100644 --- a/fe/web/.env.example +++ b/fe/web/.env.example @@ -4,6 +4,7 @@ CUSTOMER_HOST_PORT=3000 VITE_API_URL=https://qms-api.erahn.com.my NEXT_PUBLIC_API_URL=https://qms-api.erahn.com.my +VITE_BRANCH_QR_BASE_URL=https://qms-customer.erahn.com.my QR_TOKEN_SECRET=qms-branch-qr-v1 diff --git a/fe/web/.env.production b/fe/web/.env.production index 2251b05..283f8e6 100644 --- a/fe/web/.env.production +++ b/fe/web/.env.production @@ -4,10 +4,11 @@ CUSTOMER_HOST_PORT=3000 VITE_API_URL=https://qms-api.erahn.com.my NEXT_PUBLIC_API_URL=https://qms-api.erahn.com.my +VITE_BRANCH_QR_BASE_URL=https://qms-customer.erahn.com.my QR_TOKEN_SECRET=qms-branch-qr-v1 ADMIN_IMAGE=git.koppkb.com/kopkb/qms-fe-admin TELLER_IMAGE=git.koppkb.com/kopkb/qms-fe-teller CUSTOMER_IMAGE=git.koppkb.com/kopkb/qms-fe-customer -IMAGE_TAG=local \ No newline at end of file +IMAGE_TAG=local diff --git a/fe/web/admin-app/.env.example b/fe/web/admin-app/.env.example index 7fb485e..d414086 100644 --- a/fe/web/admin-app/.env.example +++ b/fe/web/admin-app/.env.example @@ -1 +1,2 @@ VITE_API_URL=https://qms-api.erahn.com.my +VITE_BRANCH_QR_BASE_URL=https://qms-customer.erahn.com.my diff --git a/fe/web/admin-app/.env.production b/fe/web/admin-app/.env.production index acb3260..d414086 100644 --- a/fe/web/admin-app/.env.production +++ b/fe/web/admin-app/.env.production @@ -1,2 +1,2 @@ -# Baked into the admin image at build time (Gitea secret VITE_API_URL) VITE_API_URL=https://qms-api.erahn.com.my +VITE_BRANCH_QR_BASE_URL=https://qms-customer.erahn.com.my diff --git a/fe/web/admin-app/Dockerfile b/fe/web/admin-app/Dockerfile index e1efb68..46d75c7 100644 --- a/fe/web/admin-app/Dockerfile +++ b/fe/web/admin-app/Dockerfile @@ -3,7 +3,9 @@ FROM node:20-bookworm AS build WORKDIR /app ARG VITE_API_URL=http://localhost:8080 +ARG VITE_BRANCH_QR_BASE_URL=http://localhost:3000 ENV VITE_API_URL=$VITE_API_URL +ENV VITE_BRANCH_QR_BASE_URL=$VITE_BRANCH_QR_BASE_URL COPY package.json package-lock.json ./ RUN npm ci diff --git a/fe/web/admin-app/src/constants.js b/fe/web/admin-app/src/constants.js index 4e843e9..e0b239f 100644 --- a/fe/web/admin-app/src/constants.js +++ b/fe/web/admin-app/src/constants.js @@ -1,6 +1,8 @@ export const SERVER_URL = import.meta.env.VITE_API_URL; -export const CUSTOMER_APP_URL = 'https://qms-customer.erahn.com.my'; +/** Public base URL encoded into branch QR codes (ManageBranchesScreen only). */ +export const BRANCH_QR_BASE_URL = + import.meta.env.VITE_BRANCH_QR_BASE_URL ?? 'http://localhost:3000'; export const ROLES = { ROLE_SUPER_ADMIN: 'ROLE_SUPER_ADMIN', diff --git a/fe/web/admin-app/src/utils/branchQr.js b/fe/web/admin-app/src/utils/branchQr.js index 31fd35c..ff6db87 100644 --- a/fe/web/admin-app/src/utils/branchQr.js +++ b/fe/web/admin-app/src/utils/branchQr.js @@ -1,5 +1,5 @@ import QRCode from 'qrcode'; -import { CUSTOMER_APP_URL } from '../constants.js'; +import { BRANCH_QR_BASE_URL } from '../constants.js'; const DEFAULT_QR_SECRET = 'qms-branch-qr-v1'; @@ -54,7 +54,7 @@ export function decodeBranchQrToken(token, secret = getQrSecret()) { } export function getBranchTicketUrl(tenantCode, branchId) { - const base = CUSTOMER_APP_URL.replace(/\/$/, ''); + const base = BRANCH_QR_BASE_URL.replace(/\/$/, ''); const token = encodeBranchQrToken(tenantCode, branchId); return `${base}/q/${token}`; } diff --git a/fe/web/docker-compose.yml b/fe/web/docker-compose.yml index 5c155ac..cb5ef0c 100644 --- a/fe/web/docker-compose.yml +++ b/fe/web/docker-compose.yml @@ -6,6 +6,7 @@ services: dockerfile: Dockerfile args: VITE_API_URL: ${VITE_API_URL:-http://localhost:8080} + VITE_BRANCH_QR_BASE_URL: ${VITE_BRANCH_QR_BASE_URL:-http://localhost:3000} container_name: qms-fe-admin restart: unless-stopped ports: