diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 25658b14..95ea822f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -4,6 +4,9 @@ on: push: branches: - dev + pull_request: + branches: + - dev jobs: build: @@ -43,6 +46,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + - name: Create app config file + run: cp app-sample.env app.env - name: Run All Tests run: go test ./... -timeout 99999s @@ -54,7 +59,11 @@ jobs: SSH_HOST: ${{ secrets.SSH_HOST }} SSH_PORT: ${{ secrets.SSH_PORT }} SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }} - PROCESS_NAME: run_development_app + SERVER_PORT: 7000 + DB_NAME: "development_db" + USERNAME: "development_user" + APP_NAME: "development" + APP_URL: "http://localhost:7000" steps: - name: SSH into server and deploy @@ -66,29 +75,16 @@ jobs: port: ${{ env.SSH_PORT }} script: | export APPROOT=~/deployments/development - export PATH=$PATH:~/.nvm/versions/node/v20.15.1/bin - export PATH=$PATH:/usr/local/go/bin - mkdir -p $APPROOT cd $APPROOT if [ -d "$APPROOT/.git" ]; then - # Navigate to the repository directory and pull changes - cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } - git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } - git pull origin dev || { echo "Failed to pull latest changes"; exit 1; } + # Navigate to the repository directory and pull changes + cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } + git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } + git pull origin dev || { echo "Failed to pull latest changes"; exit 1; } else - git clone -b dev http://github.com/${{ github.repository }} . || { echo "Failed to clone repository"; exit 1; } + git clone -b dev https://github.com/${{ github.repository}} . || { echo "Failed to clone repository"; exit 1; } fi - cp app-sample.env app.env - go build -o ~/deployments/development/development_app - - # Check if pm2 is already running - if pm2 list | grep -q "${{ env.PROCESS_NAME }}"; then - echo "Process ${{ env.PROCESS_NAME }} is running. Restarting..." - pm2 restart "${{ env.PROCESS_NAME }}" - else - echo "Process ${{ env.PROCESS_NAME }} is not running. Starting..." - pm2 start "${{ env.PROCESS_NAME }}".sh - fi + bash ./scripts/deploy_app.sh development https://github.com/${{ github.repository}} SERVER_PORT=${{ env.SERVER_PORT }} DB_NAME=${{ env.DB_NAME }} USERNAME=${{ env.USERNAME }} APP_NAME=${{ env.APP_NAME }} APP_URL=${{ env.APP_URL }} diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 06325392..000964d1 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -4,6 +4,9 @@ on: push: branches: - main + pull_request: + branches: + - main jobs: build: @@ -43,6 +46,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + - name: Create app config file + run: cp app-sample.env app.env - name: Run All Tests run: go test ./... -timeout 99999s @@ -54,7 +59,11 @@ jobs: SSH_HOST: ${{ secrets.SSH_HOST }} SSH_PORT: ${{ secrets.SSH_PORT }} SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }} - PROCESS_NAME: run_production_app + SERVER_PORT: 9000 + DB_NAME: "production_db" + USERNAME: "production_user" + APP_NAME: "production" + APP_URL: "http://localhost:9000" steps: - name: SSH into server and deploy @@ -66,29 +75,16 @@ jobs: port: ${{ env.SSH_PORT }} script: | export APPROOT=~/deployments/production - export PATH=$PATH:~/.nvm/versions/node/v20.15.1/bin - export PATH=$PATH:/usr/local/go/bin - mkdir -p $APPROOT cd $APPROOT if [ -d "$APPROOT/.git" ]; then - # Navigate to the repository directory and pull changes - cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } - git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } - git pull origin main || { echo "Failed to pull latest changes"; exit 1; } + # Navigate to the repository directory and pull changes + cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } + git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } + git pull origin main || { echo "Failed to pull latest changes"; exit 1; } else - git clone -b main http://github.com/${{ github.repository }} . || { echo "Failed to clone repository"; exit 1; } + git clone -b main https://github.com/${{ github.repository}} . || { echo "Failed to clone repository"; exit 1; } fi - cp app-sample.env app.env - go build -o ~/deployments/production/production_app - - # Check if pm2 is already running - if pm2 list | grep -q "${{ env.PROCESS_NAME }}"; then - echo "Process ${{ env.PROCESS_NAME }} is running. Restarting..." - pm2 restart "${{ env.PROCESS_NAME }}" - else - echo "Process ${{ env.PROCESS_NAME }} is not running. Starting..." - pm2 start "${{ env.PROCESS_NAME }}".sh - fi + bash ./scripts/deploy_app.sh production https://github.com/${{ github.repository}} SERVER_PORT=${{ env.SERVER_PORT }} DB_NAME=${{ env.DB_NAME }} USERNAME=${{ env.USERNAME }} APP_NAME=${{ env.APP_NAME }} APP_URL=${{ env.APP_URL }} diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index f4b49264..672235f5 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -4,6 +4,9 @@ on: push: branches: - staging + pull_request: + branches: + - staging jobs: build: @@ -43,6 +46,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + - name: Create app config file + run: cp app-sample.env app.env - name: Run All Tests run: go test ./... -timeout 99999s @@ -54,7 +59,11 @@ jobs: SSH_HOST: ${{ secrets.SSH_HOST }} SSH_PORT: ${{ secrets.SSH_PORT }} SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }} - PROCESS_NAME: run_staging_app + SERVER_PORT: 8000 + DB_NAME: "staging_db" + USERNAME: "staging_user" + APP_NAME: "staging" + APP_URL: "http://localhost:8000" steps: - name: SSH into server and deploy @@ -66,29 +75,16 @@ jobs: port: ${{ env.SSH_PORT }} script: | export APPROOT=~/deployments/staging - export PATH=$PATH:~/.nvm/versions/node/v20.15.1/bin - export PATH=$PATH:/usr/local/go/bin - mkdir -p $APPROOT cd $APPROOT if [ -d "$APPROOT/.git" ]; then - # Navigate to the repository directory and pull changes - cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } - git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } - git pull origin staging || { echo "Failed to pull latest changes"; exit 1; } + # Navigate to the repository directory and pull changes + cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } + git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } + git pull origin staging || { echo "Failed to pull latest changes"; exit 1; } else - git clone -b staging http://github.com/${{ github.repository }} . || { echo "Failed to clone repository"; exit 1; } + git clone -b staging https://github.com/${{ github.repository}} . || { echo "Failed to clone repository"; exit 1; } fi - cp app-sample.env app.env - go build -o ~/deployments/staging/staging_app - - # Check if pm2 is already running - if pm2 list | grep -q "${{ env.PROCESS_NAME }}"; then - echo "Process ${{ env.PROCESS_NAME }} is running. Restarting..." - pm2 restart "${{ env.PROCESS_NAME }}" - else - echo "Process ${{ env.PROCESS_NAME }} is not running. Starting..." - pm2 start "${{ env.PROCESS_NAME }}".sh - fi + bash ./scripts/deploy_app.sh staging https://github.com/${{ github.repository}} SERVER_PORT=${{ env.SERVER_PORT }} DB_NAME=${{ env.DB_NAME }} USERNAME=${{ env.USERNAME }} APP_NAME=${{ env.APP_NAME }} APP_URL=${{ env.APP_URL }} diff --git a/.gitignore b/.gitignore index df7969d6..42092c91 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,3 @@ _ignore/ app.env tmp/ .idea/ - -app.env \ No newline at end of file diff --git a/app-sample.env b/app-sample.env index c0f0fa28..f0383469 100644 --- a/app-sample.env +++ b/app-sample.env @@ -19,7 +19,7 @@ SSLMODE=disable USERNAME=postgres PASSWORD=password DB_NAME=db_name -MIGRATE=false +MIGRATE=true # Test # TEST_DB_HOST=localhost diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index c2497e75..728904a4 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -15,11 +15,10 @@ func SeedDatabase(db *gorm.DB) { Userid1 := utility.GenerateUUID() user1 := models.User{ - ID: Userid1, - Name: "John Doe", - Email: "john@example.com", + ID: Userid1, + Name: "John Doe", + Email: "john@example.com", Password: utility.RandomString(20), - Role: "customer", Profile: models.Profile{ ID: utility.GenerateUUID(), FirstName: "John", @@ -35,11 +34,10 @@ func SeedDatabase(db *gorm.DB) { Userid2 := utility.GenerateUUID() user2 := models.User{ - ID: Userid2, - Name: "Jane Doe", + ID: Userid2, + Name: "Jane Doe", Password: utility.RandomString(20), - Email: "jane@example.com", - Role: "customer", + Email: "jane@example.com", Profile: models.Profile{ ID: utility.GenerateUUID(), FirstName: "Jane", @@ -54,9 +52,9 @@ func SeedDatabase(db *gorm.DB) { } organisations := []models.Organisation{ - {ID: utility.GenerateUUID(), Name: "Org1", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description1", OwnerID: Userid1}, - {ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description2", OwnerID: Userid1}, - {ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description3", OwnerID: Userid2}, + {ID: utility.GenerateUUID(), Name: "Org1", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description1", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description2", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description3", OwnerID: Userid2}, } var existingUser models.User diff --git a/pkg/middleware/authorize.go b/pkg/middleware/authorize.go index 7acfee75..f4fa4ce7 100644 --- a/pkg/middleware/authorize.go +++ b/pkg/middleware/authorize.go @@ -10,7 +10,7 @@ import ( "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) -func Authorize(requiredRoles ...string) gin.HandlerFunc { +func Authorize() gin.HandlerFunc { return func(c *gin.Context) { var tokenStr string bearerToken := c.GetHeader("Authorization") @@ -49,27 +49,6 @@ func Authorize(requiredRoles ...string) gin.HandlerFunc { return } - // Check user role - userRole, ok := claims["user_role"].(string) - if !ok { - c.AbortWithStatusJSON(http.StatusForbidden, utility.BuildErrorResponse(http.StatusForbidden, "error", "Forbidden", "Unauthorized", nil)) - return - } - - // Check if user role is in the list of required roles - roleAuthorized := false - for _, role := range requiredRoles { - if userRole == role { - roleAuthorized = true - break - } - } - - if !roleAuthorized { - c.AbortWithStatusJSON(http.StatusForbidden, utility.BuildErrorResponse(http.StatusForbidden, "error", "Forbidden", "Unauthorized", nil)) - return - } - // store user claims in Context // for accesiblity in controller diff --git a/pkg/middleware/jwttoken.go b/pkg/middleware/jwttoken.go index 9c5bb9e0..9a9269d4 100644 --- a/pkg/middleware/jwttoken.go +++ b/pkg/middleware/jwttoken.go @@ -20,12 +20,10 @@ func CreateToken(user models.User) (string, time.Time, error) { //create token userid := user.ID - userRole := user.Role userClaims := jwt.MapClaims{} // specify user claims userClaims["user_id"] = userid - userClaims["user_role"] = userRole userClaims["exp"] = UnixExp userClaims["authorised"] = true diff --git a/pkg/router/organisation.go b/pkg/router/organisation.go index 4c5a80de..0a25a984 100644 --- a/pkg/router/organisation.go +++ b/pkg/router/organisation.go @@ -17,7 +17,7 @@ func Organisation(r *gin.Engine, ApiVersion string, validator *validator.Validat extReq := request.ExternalRequest{Logger: logger, Test: false} organisation := organisation.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} - organisationUrl := r.Group(fmt.Sprintf("%v", ApiVersion), middleware.Authorize("customer", "admin")) + organisationUrl := r.Group(fmt.Sprintf("%v", ApiVersion), middleware.Authorize()) { organisationUrl.POST("/organisations", organisation.CreateOrganisation) diff --git a/pkg/router/user.go b/pkg/router/user.go index fd016c55..208030e0 100644 --- a/pkg/router/user.go +++ b/pkg/router/user.go @@ -8,7 +8,6 @@ import ( "github.com/hngprojects/hng_boilerplate_golang_web/external/request" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/user" - "github.com/hngprojects/hng_boilerplate_golang_web/pkg/middleware" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) @@ -21,7 +20,6 @@ func User(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *s { userUrl.POST("/users/signup", user.CreateUser) userUrl.POST("/users/login", user.LoginUser) - userUrl.GET("/customers", middleware.Authorize("admin"), user.GetAllCustomers) } return r } diff --git a/scripts/deploy_app.sh b/scripts/deploy_app.sh new file mode 100644 index 00000000..19edaf36 --- /dev/null +++ b/scripts/deploy_app.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e + +if [ $# -lt 2 ]; then + echo "Usage: $0 [env_vars...]" + exit 1 +fi + +ENV=$1 +GITHUB_REPOSITORY=$2 +shift 2 + +case $ENV in + development) + BRANCH="dev" + APPROOT=~/deployments/development + APP_NAME="development_app" + ;; + staging) + BRANCH="staging" + APPROOT=~/deployments/staging + APP_NAME="staging_app" + ;; + production) + BRANCH="main" + APPROOT=~/deployments/production + APP_NAME="production_app" + ;; + *) + echo "Invalid environment: $ENV" + exit 1 + ;; +esac + +export APPROOT +export PATH=$PATH:~/.nvm/versions/node/v20.15.1/bin +export PATH=$PATH:/usr/local/go/bin + +mkdir -p $APPROOT +cd $APPROOT + +if [ -d "$APPROOT/.git" ]; then + # Navigate to the repository directory and pull changes + cd $APPROOT || { echo "Failed to navigate to web root directory"; exit 1; } + git reset --hard HEAD || { echo "Failed to reset local changes"; exit 1; } + git pull origin $BRANCH || { echo "Failed to pull latest changes"; exit 1; } +else + git clone -b $BRANCH https://github.com/$GITHUB_REPOSITORY . || { echo "Failed to clone repository"; exit 1; } +fi + +# Replace environment variables in app.env +cp app-sample.env app.env +for var in "$@" +do + # Split the variable into key and value + KEY=${var%%=*} + VALUE=${var#*=} + + # Check if the key already exists in the file + if grep -q "^$KEY=" app.env; then + # Update the existing key with the new value + sed -i "s|^$KEY=.*|$KEY=$VALUE|" app.env + else + # Add the new key-value pair + echo "$KEY=$VALUE" >> app.env + fi +done + +go build -o $APPROOT/$APP_NAME + +# Start or restart the built binary with pm2 +if pm2 list | grep -qw "$APP_NAME"; then + echo "Process $APP_NAME is running. Restarting..." + pm2 restart $APPROOT/$APP_NAME +else + echo "Process $APP_NAME is not running. Starting..." + pm2 start $APPROOT/$APP_NAME --name $APP_NAME +fi \ No newline at end of file