diff --git a/.github/workflows/api-container.yml b/.github/workflows/api-container.yml new file mode 100644 index 0000000..37244b7 --- /dev/null +++ b/.github/workflows/api-container.yml @@ -0,0 +1,95 @@ +name: API Deployment Container +on: + workflow_dispatch: {} + pull_request: + branches: + - main + types: + - opened + - reopened + - synchronize + - ready_for_review + push: + branches: + - main + paths: + - api/** +jobs: + determine-workflow: + runs-on: 'ubuntu-latest' + outputs: + workflow_type: ${{ steps.workflow.outputs.workflow_type }} + workflow_envs: ${{ steps.workflow.outputs.workflow_envs }} + release_type: ${{ steps.workflow.outputs.release_type }} + current_version: ${{ steps.version.outputs.current_version }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Determine Workflow + id: workflow + shell: bash + run: | + event=${{ github.event_name }} + workflow_type='dev'; + workflow_envs='["dev"]' + if [[ $event == 'workflow_dispatch' && '${{ github.ref_name }}' == 'main' ]]; + then + echo "in if statement" + workflow_type='release'; + workflow_envs='["prod"]' + fi + + echo "workflow_type=$workflow_type" >> $GITHUB_OUTPUT + echo "workflow_envs=$workflow_envs" >> $GITHUB_OUTPUT + + echo "Running $workflow_type pipeline in environments: $workflow_envs" >> $GITHUB_STEP_SUMMARY + - name: Extract Version + id: version + shell: bash + run: | + version=$(jq -r '.version' api/package.json) + echo "current_version=$version" >> "$GITHUB_OUTPUT" + nonprod-deploy: + needs: determine-workflow + if: needs.determine-workflow.outputs.workflow_type != 'release' + strategy: + max-parallel: 1 + matrix: + env: ${{ fromJson(needs.determine-workflow.outputs.workflow_envs) }} + uses: ./.github/workflows/api-deploy-nonprod.yml + with: + environments: ${{ matrix.env }} + workflow_type: ${{ needs.determine-workflow.outputs.workflow_type }} + branch: ${{ github.head_ref || github.ref_name }} + current_version: ${{ needs.determine-workflow.outputs.current_version }} + secrets: + DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + TEST: ${{ secrets.TEST }} + COOLIFY_WEBHOOK_API: ${{ secrets.COOLIFY_WEBHOOK_API }} + COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }} + permissions: + contents: read + packages: write + prod-deploy: + needs: determine-workflow + if: needs.determine-workflow.outputs.workflow_type == 'release' + strategy: + max-parallel: 1 + matrix: + env: ${{ fromJson(needs.determine-workflow.outputs.workflow_envs) }} + uses: ./.github/workflows/api-deploy-prod.yml + with: + environments: ${{ matrix.env }} + workflow_type: ${{ needs.determine-workflow.outputs.workflow_type }} + branch: ${{ github.head_ref || github.ref_name }} + current_version: ${{ needs.determine-workflow.outputs.current_version }} + secrets: + DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + TEST: ${{ secrets.TEST }} + COOLIFY_WEBHOOK_API: ${{ secrets.COOLIFY_WEBHOOK_API }} + COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }} + permissions: + contents: read + packages: write diff --git a/.github/workflows/api-deploy-nonprod.yml b/.github/workflows/api-deploy-nonprod.yml new file mode 100644 index 0000000..ff3547c --- /dev/null +++ b/.github/workflows/api-deploy-nonprod.yml @@ -0,0 +1,94 @@ +name: API Deployment Non-Production +on: + workflow_call: + inputs: + environments: + type: string + description: An optional list of environments to deploy to. + default: 'dev' + workflow_type: + type: string + description: An optional string for workflow types. + default: 'dev' + branch: + type: string + description: An optional string to define which branch to checkout. + default: 'main' + current_version: + type: string + description: Current Version of the package.json. + default: '0.0.0' + secrets: + DOCKERHUB_USER: {} + DOCKERHUB_TOKEN: {} + TEST: {} + COOLIFY_WEBHOOK_API: {} + COOLIFY_TOKEN: {} +jobs: + check-inputs: + runs-on: 'ubuntu-latest' + environment: ${{ inputs.environments }} + steps: + - name: Check secrets present + run: | + if [[ -z "${{ secrets.COOLIFY_WEBHOOK_API }}" ]]; then + echo "COOLIFY_WEBHOOK_API secret is empty or missing" + exit 1 + else + echo "COOLIFY_WEBHOOK_API secret is set" + fi + if [[ -z "${{ secrets.COOLIFY_TOKEN }}" ]]; then + echo "COOLIFY_TOKEN secret is empty or missing" + exit 1 + else + echo "COOLIFY_TOKEN secret is set" + fi + if [[ -z "${{ secrets.DOCKERHUB_USER }}" ]]; then + echo "DOCKERHUB_USER secret is empty or missing" + exit 1 + else + echo "DOCKERHUB_USER secret is set" + fi + if [[ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]]; then + echo "DOCKERHUB_TOKEN secret is empty or missing" + exit 1 + else + echo "DOCKERHUB_TOKEN secret is set" + fi + echo "Current Version: ${{inputs.current_version}}" + build: + needs: check-inputs + if: needs.check-inputs.result == 'success' && inputs.workflow_type != 'release' + environment: ${{ inputs.environments }} + runs-on: 'ubuntu-latest' + permissions: + contents: read + packages: write + steps: + - name: Branch Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + - name: Login to Docker + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Backend + run: docker build -f api/Dockerfile -t john4064/shiftsync:latest_api ./api --build-arg ENVIRONMENT=dev + - name: Docker Push Backend + run: docker push john4064/shiftsync:latest_api + deploy: + needs: build + if: needs.build.result == 'success' && inputs.workflow_type != 'release' + environment: ${{ inputs.environments }} + runs-on: 'ubuntu-latest' + permissions: + contents: read + packages: write + steps: + - name: Deploy to Coolify + run: | + curl --request GET '${{ secrets.COOLIFY_WEBHOOK_API }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' + diff --git a/.github/workflows/api-deploy-prod.yml b/.github/workflows/api-deploy-prod.yml new file mode 100644 index 0000000..63ff524 --- /dev/null +++ b/.github/workflows/api-deploy-prod.yml @@ -0,0 +1,94 @@ +name: API Deployment Production +on: + workflow_call: + inputs: + environments: + type: string + description: An optional list of environments to deploy to. + default: 'prod' + workflow_type: + type: string + description: An optional string for workflow types. + default: 'prod' + branch: + type: string + description: An optional string to define which branch to checkout. + default: 'main' + current_version: + type: string + description: Current Version of the package.json. + default: '0.0.0' + secrets: + DOCKERHUB_USER: {} + DOCKERHUB_TOKEN: {} + TEST: {} + COOLIFY_WEBHOOK_API: {} + COOLIFY_TOKEN: {} +jobs: + check-inputs: + runs-on: 'ubuntu-latest' + environment: ${{ inputs.environments }} + steps: + - name: Check secrets present + run: | + if [[ -z "${{ secrets.COOLIFY_WEBHOOK_API }}" ]]; then + echo "COOLIFY_WEBHOOK_API secret is empty or missing" + exit 1 + else + echo "COOLIFY_WEBHOOK_API secret is set" + fi + if [[ -z "${{ secrets.COOLIFY_TOKEN }}" ]]; then + echo "COOLIFY_TOKEN secret is empty or missing" + exit 1 + else + echo "COOLIFY_TOKEN secret is set" + fi + if [[ -z "${{ secrets.DOCKERHUB_USER }}" ]]; then + echo "DOCKERHUB_USER secret is empty or missing" + exit 1 + else + echo "DOCKERHUB_USER secret is set" + fi + if [[ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]]; then + echo "DOCKERHUB_TOKEN secret is empty or missing" + exit 1 + else + echo "DOCKERHUB_TOKEN secret is set" + fi + echo "Current Version: ${{inputs.current_version}}" + build: + needs: check-inputs + if: needs.check-inputs.result == 'success' && inputs.workflow_type == 'release' + environment: ${{ inputs.environments }} + runs-on: 'ubuntu-latest' + permissions: + contents: read + packages: write + steps: + - name: Branch Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + - name: Login to Docker + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Backend + run: docker build -f api/Dockerfile -t john4064/shiftsync:prod_api ./api --build-arg ENVIRONMENT=prod + - name: Docker Push Backend + run: docker push john4064/shiftsync:prod_api + deploy: + needs: build + if: needs.build.result == 'success' && inputs.workflow_type == 'release' + environment: ${{ inputs.environments }} + runs-on: 'ubuntu-latest' + permissions: + contents: read + packages: write + steps: + - name: Deploy to Coolify + run: | + curl --request GET '${{ secrets.COOLIFY_WEBHOOK_API }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' + diff --git a/.github/workflows/web-container.yml b/.github/workflows/web-container.yml index 095eeec..61aeca7 100644 --- a/.github/workflows/web-container.yml +++ b/.github/workflows/web-container.yml @@ -66,7 +66,7 @@ jobs: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} TEST: ${{ secrets.TEST }} - COOLIFY_WEBHOOK: ${{ secrets.COOLIFY_WEBHOOK }} + COOLIFY_WEBHOOK_WEB: ${{ secrets.COOLIFY_WEBHOOK_WEB }} COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }} permissions: contents: read @@ -88,7 +88,7 @@ jobs: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} TEST: ${{ secrets.TEST }} - COOLIFY_WEBHOOK: ${{ secrets.COOLIFY_WEBHOOK }} + COOLIFY_WEBHOOK_WEB: ${{ secrets.COOLIFY_WEBHOOK_WEB }} COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }} permissions: contents: read diff --git a/.github/workflows/web-deploy-nonprod.yml b/.github/workflows/web-deploy-nonprod.yml index 78c73f2..61fcb6b 100644 --- a/.github/workflows/web-deploy-nonprod.yml +++ b/.github/workflows/web-deploy-nonprod.yml @@ -22,7 +22,7 @@ on: DOCKERHUB_USER: {} DOCKERHUB_TOKEN: {} TEST: {} - COOLIFY_WEBHOOK: {} + COOLIFY_WEBHOOK_WEB: {} COOLIFY_TOKEN: {} jobs: check-inputs: @@ -31,11 +31,11 @@ jobs: steps: - name: Check secrets present run: | - if [[ -z "${{ secrets.COOLIFY_WEBHOOK }}" ]]; then - echo "COOLIFY_WEBHOOK secret is empty or missing" + if [[ -z "${{ secrets.COOLIFY_WEBHOOK_WEB }}" ]]; then + echo "COOLIFY_WEBHOOK_WEB secret is empty or missing" exit 1 else - echo "COOLIFY_WEBHOOK secret is set" + echo "COOLIFY_WEBHOOK_WEB secret is set" fi if [[ -z "${{ secrets.COOLIFY_TOKEN }}" ]]; then echo "COOLIFY_TOKEN secret is empty or missing" @@ -55,7 +55,7 @@ jobs: else echo "DOCKERHUB_TOKEN secret is set" fi - echo "${{inputs.current_version}}" + echo "Current Version: ${{inputs.current_version}}" build: needs: check-inputs if: needs.check-inputs.result == 'success' && inputs.workflow_type != 'release' @@ -90,5 +90,5 @@ jobs: steps: - name: Deploy to Coolify run: | - curl --request GET '${{ secrets.COOLIFY_WEBHOOK }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' + curl --request GET '${{ secrets.COOLIFY_WEBHOOK_WEB }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' diff --git a/.github/workflows/web-deploy-prod.yml b/.github/workflows/web-deploy-prod.yml index 8b1c638..6c87dcc 100644 --- a/.github/workflows/web-deploy-prod.yml +++ b/.github/workflows/web-deploy-prod.yml @@ -22,7 +22,7 @@ on: DOCKERHUB_USER: {} DOCKERHUB_TOKEN: {} TEST: {} - COOLIFY_WEBHOOK: {} + COOLIFY_WEBHOOK_WEB: {} COOLIFY_TOKEN: {} jobs: check-inputs: @@ -31,11 +31,11 @@ jobs: steps: - name: Check secrets present run: | - if [[ -z "${{ secrets.COOLIFY_WEBHOOK }}" ]]; then - echo "COOLIFY_WEBHOOK secret is empty or missing" + if [[ -z "${{ secrets.COOLIFY_WEBHOOK_WEB }}" ]]; then + echo "COOLIFY_WEBHOOK_WEB secret is empty or missing" exit 1 else - echo "COOLIFY_WEBHOOK secret is set" + echo "COOLIFY_WEBHOOK_WEB secret is set" fi if [[ -z "${{ secrets.COOLIFY_TOKEN }}" ]]; then echo "COOLIFY_TOKEN secret is empty or missing" @@ -55,7 +55,7 @@ jobs: else echo "DOCKERHUB_TOKEN secret is set" fi - echo "${{inputs.current_version}}" + echo "Current Version: ${{inputs.current_version}}" build: needs: check-inputs if: needs.check-inputs.result == 'success' && inputs.workflow_type == 'release' @@ -90,5 +90,5 @@ jobs: steps: - name: Deploy to Coolify run: | - curl --request GET '${{ secrets.COOLIFY_WEBHOOK }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' + curl --request GET '${{ secrets.COOLIFY_WEBHOOK_WEB }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}' diff --git a/api/.dockerignore b/api/.dockerignore new file mode 100644 index 0000000..0979a6c --- /dev/null +++ b/api/.dockerignore @@ -0,0 +1 @@ +**/node_modules/** diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 0000000..b6db0c2 --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,14 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY ./package*.json ./ + +RUN npm ci + +COPY . ./ + +EXPOSE 5172 +EXPOSE 5170 + +CMD npm run dev diff --git a/api/docker-compose.yaml b/api/docker-compose.yaml new file mode 100644 index 0000000..347119c --- /dev/null +++ b/api/docker-compose.yaml @@ -0,0 +1,14 @@ +services: + shiftsync-web: + image: 'docker.io/john4064/shiftsync:prod_api' + environment: + - 'TESTVAR=${COOLIFY_VAR}' + volumes: + - /home/jparkhurst/shiftsync:/shiftsync + ports: + - "5172:5172" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:5172"] + interval: 10s + timeout: 5s + retries: 5 \ No newline at end of file diff --git a/api/server.js b/api/server.js index a46912d..fda9b50 100644 --- a/api/server.js +++ b/api/server.js @@ -7,7 +7,11 @@ import { postgresServices } from './services/postgres/postgresServices.js'; const app = express(); const corsOptions = { - origin: ["http://localhost:5173"] + origin: [ + "http://localhost:5173", + "https://shift-dev.code-catalyst.com", + "https://shift.code-catalyst.com" + ] }; app.use(cors(corsOptions)); diff --git a/web/src/router/AppRouter.jsx b/web/src/router/AppRouter.jsx index 5fa17a6..d5dfcb5 100644 --- a/web/src/router/AppRouter.jsx +++ b/web/src/router/AppRouter.jsx @@ -63,7 +63,7 @@ const AppRouter = () => { const fetchAPI = async () => { const location = window.location; - const uri = `${location?.protocol}//${location?.hostname}${isDev ? ':5172' : ''}/api`; + const uri = `${location?.protocol}//${location.hostname}/api`; const response = await axios.get(uri); console.log(response.data.fruits); };