diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 20a83ed3..dc06e527 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,9 +1,11 @@ name: CI on: + push: + branches: [dev, staging, main] pull_request: - branches: - - dev + types: [opened, synchronize, reopened] + branches: [dev, staging, main] jobs: test: @@ -25,5 +27,6 @@ jobs: run: yarn test env: CI: true - - name: buld the dist + + - name: Build the dist run: yarn build diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 25ae43b0..9acefc15 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -1,40 +1,61 @@ -name: Build, Test, and Deploy for Dev Branch +name: Deploy to Dev on: - push: - branches: - - dev + workflow_run: + workflows: [CI] + types: + - completed + branches: [dev] jobs: - build: - runs-on: self-hosted - defaults: - run: - working-directory: /var/www/aihomework/dev - + deploy: + runs-on: ubuntu-latest steps: - - name: Pull from github - id: pull - run: | - git stash - git pull origin dev - - - name: install dependencies - run: yarn install + - name: Checkout repository + uses: actions/checkout@v4 + + # - name: Install sshpass + # run: sudo apt-get install sshpass - - name: Run Test - run: yarn test - - - name: buld the dist - run: yarn build + - name: Fetch .env file from server + run: | + sshpass -p ${{ secrets.PASSWORD }} scp -o StrictHostKeyChecking=no ${{ secrets.USERNAME }}@${{ secrets.HOST }}:/var/www/aihomework/dev/.env .env + env: + SSH_HOST: ${{ secrets.HOST }} + SSH_USERNAME: ${{ secrets.USERNAME }} + SSH_PASSWORD: ${{ secrets.PASSWORD }} - - name: migrate - run: yarn reset-db + - name: Build Docker images + run: | + docker compose --env-file .env -f docker-compose.yml build - - name: setup service file - run: sudo cp server-script/aihomeworkdev.service /etc/systemd/system + - name: List Docker images + run: docker images - - name: start the app + - name: Save Docker images to tarball run: | - sudo systemctl daemon-reload - sudo systemctl restart aihomeworkdev + docker save hng_boilerplate_expressjs-backend:latest | gzip > dev-images.tar.gz + + - name: Copy Docker images to server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "dev-images.tar.gz" + target: "~/images-tar" + + - name: Deploy to server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd ~/dev-deployment/hng_boilerplate_expressjs + git stash + git checkout dev + git pull + docker load -i ~/images-tar/dev-images.tar.gz + docker compose -f docker-compose.yml down + docker compose -f docker-compose.yml up -d diff --git a/.github/workflows/pr-deploy.yml b/.github/workflows/pr-deploy.yml index 4a000022..135f1e38 100644 --- a/.github/workflows/pr-deploy.yml +++ b/.github/workflows/pr-deploy.yml @@ -10,15 +10,13 @@ jobs: name: preview url: ${{ steps.deploy.outputs.preview-url }} runs-on: ubuntu-latest - steps: - - name: Checkout to branch + - name: Checkout the branch uses: actions/checkout@v4 - id: deploy name: Pull Request Deploy uses: hngprojects/pr-deploy@main-patch - with: server_host: ${{ secrets.SERVER_HOST }} server_username: ${{ secrets.SERVER_USERNAME }} diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 64a6096e..df8f28ae 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -1,35 +1,74 @@ -name: Build, Test, and Deploy for Prod Branch +name: Deploy to Prod on: - push: - branches: [main] + workflow_run: + workflows: [CI] + types: + - completed + branches: [prod] jobs: - build: - runs-on: self-hosted - defaults: - run: - working-directory: /var/www/aihomework/prod - + on-success: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - name: Pull from GitHub + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install sshpass + run: sudo apt-get install sshpass + + - name: Fetch .env file from server + run: | + sshpass -p ${{ secrets.PASSWORD }} scp -o StrictHostKeyChecking=no ${{ secrets.USERNAME }}@${{ secrets.HOST }}:~/prod-deployment/hng_boilerplate_expressjs/.env .env + env: + SSH_HOST: ${{ secrets.HOST }} + SSH_USERNAME: ${{ secrets.USERNAME }} + SSH_PASSWORD: ${{ secrets.PASSWORD }} + + - name: Build Docker images run: | - git stash - git pull origin main + docker compose --env-file .env -f docker-compose.production.yml build - - name: Install dependencies - run: yarn install + - name: List Docker images + run: docker images - - name: Build the dist - run: yarn build + - name: Save Docker images to tarball + run: | + docker save hng_boilerplate_expressjs-backend_prod:latest | gzip > prod-images.tar.gz - - name: migrate - run: yarn migrate + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: prod-images + path: prod-images.tar.gz - - name: Setup service file - run: sudo cp server-script/aihomeworkprod.service /etc/systemd/system + - name: Copy Docker images to server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "prod-images.tar.gz" + target: "~/images-tar" - - name: Start the app - run: | - sudo systemctl daemon-reload - sudo systemctl restart aihomeworkprod + - name: Deploy to server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd ~/prod-deployment/hng_boilerplate_expressjs + git stash + git checkout prod + git pull + docker load -i ~/images-tar/prod-images.tar.gz + docker compose -f docker-compose.production.yml down + docker compose -f docker-compose.production.yml up -d + + on-failure: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + steps: + - run: echo "CI Workflow failed. Prod deployment was not triggered." \ No newline at end of file diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml new file mode 100644 index 00000000..1333d15a --- /dev/null +++ b/.github/workflows/staging.yml @@ -0,0 +1,58 @@ +name: Deploy to staging + +on: + workflow_run: + workflows: [CI] + types: + - completed + branches: [staging] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch .env file from server + run: | + sshpass -p ${{ secrets.PASSWORD }} scp -o StrictHostKeyChecking=no ${{ secrets.USERNAME }}@${{ secrets.HOST }}:~/staging-deployment/hng_boilerplate_expressjs/.env .env + env: + SSH_HOST: ${{ secrets.HOST }} + SSH_USERNAME: ${{ secrets.USERNAME }} + SSH_PASSWORD: ${{ secrets.PASSWORD }} + + - name: Build Docker images + run: | + docker compose --env-file .env -f docker-compose.staging.yml build + + - name: List Docker images + run: docker images + + - name: Save Docker images to tarball + run: | + docker save hng_boilerplate_expressjs-backend_staging:latest | gzip > staging-images.tar.gz + + - name: Copy Docker images to server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "staging-images.tar.gz" + target: "~/images-tar" + + - name: Deploy to server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd ~/staging-deployment/hng_boilerplate_expressjs + git stash + git checkout staging + git pull + docker load -i ~/images-tar/staging-images.tar.gz + docker compose -f docker-compose.staging.yml down + docker compose -f docker-compose.staging.yml up -d diff --git a/docker-compose.production.yml b/docker-compose.production.yml index aaf94395..82ba1d0c 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,15 +1,14 @@ services: - backend: + backend_prod: container_name: backend_prod build: context: . - args: - - NODE_ENV=${NODE_ENV} ports: - 4444:8000 env_file: - .env environment: + NODE_ENV: production DB_HOST: backend_db_prod DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@backend_db_prod:5432/${DB_NAME} networks: diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index c90be1a8..3e9dba04 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -1,15 +1,14 @@ services: - backend: + backend_staging: container_name: backend_staging build: context: . - args: - - NODE_ENV=${NODE_ENV} ports: - 3333:8000 env_file: - .env environment: + NODE_ENV: staging DB_HOST: backend_db_staging DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@backend_db_staging:5432/${DB_NAME} networks: diff --git a/docker-compose.yml b/docker-compose.yml index 3d8181b4..89f740c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,15 @@ +version: "3.8" services: backend: build: context: . - args: - - NODE_ENV=${NODE_ENV} ports: - 2222:8000 env_file: - .env environment: + NODE_ENV: development + DB_HOST: backend_db DATABASE_URL: postgres:// ${DB_USER}:${DB_PASSWORD}@backend_db:5432/${DB_NAME} networks: - app diff --git a/server-script/aihomeworkstaging.service b/server-script/aihomeworkstaging.service new file mode 100644 index 00000000..831038c2 --- /dev/null +++ b/server-script/aihomeworkstaging.service @@ -0,0 +1,13 @@ +[Unit] +Description=AIHomework-Dev +After=network.target + +[Service] +WorkingDirectory=/var/www/aihomework/staging +ExecStart=/bin/bash /var/www/aihomework/dev/server-script/startappstaging.sh +#Restart=on-failure +#RestartSec=20s +StartLimitInterval=0 + +[Install] +WantedBy=multi-user.target diff --git a/server-script/startappstaging.sh b/server-script/startappstaging.sh new file mode 100644 index 00000000..9e056b30 --- /dev/null +++ b/server-script/startappstaging.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd /var/www/aihomework/staging/ +mkdir -p logs +/usr/bin/yarn start >> logs/stagingoutput.log 2>&1 diff --git a/src/models/faq.ts b/src/models/faq.ts index 86932bec..8f570d22 100644 --- a/src/models/faq.ts +++ b/src/models/faq.ts @@ -1,4 +1,10 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; import ExtendedBaseEntity from "./extended-base-entity"; import { UserRole } from "../enums/userRoles"; @@ -18,6 +24,12 @@ class FAQ extends ExtendedBaseEntity { @Column({ nullable: false, default: UserRole.SUPER_ADMIN }) createdBy: string; + + @CreateDateColumn() + created_at: Date; + + @UpdateDateColumn() + updated_at: Date; } export { FAQ }; diff --git a/src/models/user.ts b/src/models/user.ts index 03dc0d22..ad88e983 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -68,6 +68,9 @@ export class User extends ExtendedBaseEntity { @Column({ nullable: true }) otp: number; + @Column({ default: false }) + is_superadmin: boolean; + @Column({ nullable: true }) otp_expires_at: Date;