From 4ec1d3429742ce1adacc9357d3bf0be58499d551 Mon Sep 17 00:00:00 2001 From: JakkrapartXD Date: Mon, 9 Feb 2026 16:56:12 +0700 Subject: [PATCH] add workflow --- .forgejo/workflows/deploy-backend.yml | 64 +++++++++++++++++ .../workflows/deploy-frontend-learner.yml | 64 +++++++++++++++++ .../workflows/deploy-frontend-management.yml | 64 +++++++++++++++++ Backend/Dockerfile | 55 +++++++++++++++ compose.infa.yaml | 69 +++++++++++++++++++ compose.yaml | 48 +++++++++++++ 6 files changed, 364 insertions(+) create mode 100644 .forgejo/workflows/deploy-backend.yml create mode 100644 .forgejo/workflows/deploy-frontend-learner.yml create mode 100644 .forgejo/workflows/deploy-frontend-management.yml create mode 100644 Backend/Dockerfile create mode 100644 compose.infa.yaml create mode 100644 compose.yaml diff --git a/.forgejo/workflows/deploy-backend.yml b/.forgejo/workflows/deploy-backend.yml new file mode 100644 index 00000000..290fb69d --- /dev/null +++ b/.forgejo/workflows/deploy-backend.yml @@ -0,0 +1,64 @@ +name: Build and Deploy Backend + +on: + push: + branches: + - main + - dev + paths: + - 'Backend/**' + workflow_dispatch: + +jobs: + build: + name: Build Backend Docker Image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ vars.DOCKER_REGISTRY }}/elearning-backend + tags: | + type=ref,event=branch + type=sha,prefix= + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./Backend + file: ./Backend/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy: + name: Deploy Backend to Server + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + steps: + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: eval "${{ secrets.SSH_DEPLOY_BACKEND_CMD }}" diff --git a/.forgejo/workflows/deploy-frontend-learner.yml b/.forgejo/workflows/deploy-frontend-learner.yml new file mode 100644 index 00000000..b468dbac --- /dev/null +++ b/.forgejo/workflows/deploy-frontend-learner.yml @@ -0,0 +1,64 @@ +name: Build and Deploy Frontend Learner + +on: + push: + branches: + - main + - dev + paths: + - 'Frontend-Learner/**' + workflow_dispatch: + +jobs: + build: + name: Build Frontend Learner Docker Image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ vars.DOCKER_REGISTRY }}/elearning-frontend-learner + tags: | + type=ref,event=branch + type=sha,prefix= + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./Frontend-Learner + file: ./Frontend-Learner/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy: + name: Deploy Frontend Learner to Server + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + steps: + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: eval "${{ secrets.SSH_DEPLOY_FRONTEND_LEARNER_CMD }}" diff --git a/.forgejo/workflows/deploy-frontend-management.yml b/.forgejo/workflows/deploy-frontend-management.yml new file mode 100644 index 00000000..8bc97d5f --- /dev/null +++ b/.forgejo/workflows/deploy-frontend-management.yml @@ -0,0 +1,64 @@ +name: Build and Deploy Frontend Management + +on: + push: + branches: + - main + - dev + paths: + - 'frontend_management/**' + workflow_dispatch: + +jobs: + build: + name: Build Frontend Management Docker Image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ vars.DOCKER_REGISTRY }}/elearning-frontend-management + tags: | + type=ref,event=branch + type=sha,prefix= + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./frontend_management + file: ./frontend_management/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy: + name: Deploy Frontend Management to Server + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + steps: + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: eval "${{ secrets.SSH_DEPLOY_FRONTEND_MGMT_CMD }}" diff --git a/Backend/Dockerfile b/Backend/Dockerfile new file mode 100644 index 00000000..1d8e0273 --- /dev/null +++ b/Backend/Dockerfile @@ -0,0 +1,55 @@ +# Build stage +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ +COPY pnpm-lock.yaml* ./ + +# Install pnpm and dependencies +RUN npm install -g pnpm && pnpm install --frozen-lockfile + +# Copy source code +COPY . . + +# Generate Prisma client +RUN pnpm prisma:generate + +# Build application +RUN pnpm build + +# Production stage +FROM node:20-alpine AS production + +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm + +# Copy package files +COPY package*.json ./ +COPY pnpm-lock.yaml* ./ + +# Install production dependencies only +RUN pnpm install --prod --frozen-lockfile + +# Copy built files from builder +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/public ./public + +# Set environment +ENV NODE_ENV=production +ENV PORT=4000 + +# Expose port +EXPOSE 4000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:4000/health || exit 1 + +# Start application +CMD ["node", "dist/server.js"] diff --git a/compose.infa.yaml b/compose.infa.yaml new file mode 100644 index 00000000..9d68ef7d --- /dev/null +++ b/compose.infa.yaml @@ -0,0 +1,69 @@ +services: + # PostgreSQL Database + postgres: + image: postgres:16-alpine + container_name: elearning-postgres + restart: unless-stopped + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=elearning_dev + - POSTGRES_PASSWORD=12345678 + - TZ=Asia/Bangkok + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 10s + timeout: 5s + retries: 5 + networks: + - elearning-shared + + # MinIO - S3 Compatible Storage + minio: + image: minio/minio:latest + container_name: elearning-minio + deploy: + resources: + limits: + memory: 1G + restart: unless-stopped + command: server /data --console-address ":9001" + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio-data:/data + environment: + - TZ=Asia/Bangkok + - MINIO_ROOT_USER=admin + - MINIO_ROOT_PASSWORD=12345678 + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ] + interval: 30s + timeout: 20s + retries: 3 + networks: + - elearning-shared + + # Mailhog - Email Testing + mailhog: + image: mailhog/mailhog:latest + container_name: elearning-mailhog + restart: unless-stopped + ports: + - "1025:1025" + - "8025:8025" + environment: + - TZ=Asia/Bangkok + networks: + - elearning-shared + +volumes: + postgres-data: + minio-data: + +networks: + elearning-shared: + external: true diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 00000000..c8445265 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,48 @@ +services: + # ============================================ + # Application Services + # ============================================ + + # Backend API + backend: + image: 192.168.1.60:5000/elearning-backend:latest + container_name: elearning-backend + restart: unless-stopped + ports: + - "4000:4000" + env_file: + - .env + networks: + - elearning-shared + + # Frontend Learner + frontend-learner: + image: 192.168.1.60:5000/elearning-frontend-learner:latest + container_name: elearning-frontend-learner + restart: unless-stopped + ports: + - "3000:3000" + env_file: + - .env + depends_on: + - backend + networks: + - elearning-shared + + # Frontend Management + frontend-management: + image: 192.168.1.60:5000/elearning-frontend-management:latest + container_name: elearning-frontend-management + restart: unless-stopped + ports: + - "3001:3000" + env_file: + - .env + depends_on: + - backend + networks: + - elearning-shared + +networks: + elearning-shared: + external: true \ No newline at end of file