diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..fa0e6e2b2 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,45 @@ +name: Docker Compose Deployment Workflow + +on: + push: + branches: [ujwal-cicd, ujwal-docker] + paths: + - ".github/workflows/docker.yml" + - "backend/**" + - "frontend/**" + - ".github/workflows/docker.yml" + pull_request: + types: [synchronize] + workflow_dispatch: + +defaults: + run: + shell: bash + +permissions: + actions: read + contents: read + +jobs: + Docker-Compose-Deployment: + runs-on: self-hosted-runner + steps: + + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + + - name: Setup Env variables + run: | + echo -e ${{ secrets.FRONTEND_ENV }} > frontend/.env.local + echo -e ${{ secrets.BACKEND_ENV }} > backend/.env + + - name: Destroy Previous Deployment + run: docker compose down + + - name: Apply New Deployment + run: docker compose up -d + + - name: Restart Nginx + run: sudo systemctl restart nginx \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..918115569 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,267 @@ +name: ChatGPT Workflow + +on: + push: + branches: + - main + - ujwal-devops + - ujwal-cicd + paths: + - ".github/workflows/main.yml" + - "backend/**" + - "frontend/**" + - "images/**" + - "prompts/**" + pull_request: + types: [synchronize] + workflow_dispatch: + +defaults: + run: + shell: bash + +permissions: + actions: write + contents: write + +jobs: + SonarQube-Analysis: + runs-on: ubuntu-24.04 + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + - name: Setup Environment variables + run: echo -e ${{ secrets.SONAR_PROPERTIES }} > sonar-project.properties + - name: Sonar Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + Build-App-Backend: + runs-on: ubuntu-22.04 + needs: [SonarQube-Analysis] + defaults: + run: + shell: bash + working-directory: backend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + - name: Setup Python Environment + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install PM2 + run: npm install -g pm2 + - name: Setup Env variables + run: | + echo -e ${{ secrets.BACKEND_ENV }} > .env + - name: Install Dependencies + run: pwd && pip install -r dependencies.txt + - name: Make Migrations + run: python manage.py migrate + - name: Collect Static Content + run: python manage.py collectstatic + - name: Start App + run: pm2 start "gunicorn backend.wsgi:application -b 0.0.0.0:8000 --reload" --name backend + + Build-App-Frontend: + runs-on: ubuntu-22.04 + needs: [SonarQube-Analysis] + defaults: + run: + shell: bash + working-directory: frontend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + - name: Setup node Environment + uses: actions/setup-node@v4 + with: + node-version: 18.20.5 + - name: Install PM2 + run: npm install -g pm2 + - name: Setup Env variables + run: | + echo -e ${{ secrets.FRONTEND_ENV }} > .env.local + - name: Install Dependencies + run: pwd && npm install + - name: Static Build + run: npm run build + - name: Start App + run: pm2 start "npm start" --name frontend + + Image-Vuln-Check-Backend: + runs-on: ubuntu-24.04 + needs: [Build-App-Backend] + continue-on-error: true + defaults: + run: + shell: bash + working-directory: backend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Docker Image + run: docker build -t docker.io/${{ vars.DOCKERHUB_USERNAME }}/chatgpt-backend:${{ github.sha }} . + - name: Run Trivy Vulnerability Scanner + uses: aquasecurity/trivy-action@0.20.0 + with: + image-ref: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/chatgpt-backend:${{ github.sha }}" + format: "table" + exit-code: "1" + ignore-unfixed: true + output: trivy-report-backend.txt + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: trivy-report-backend + path: trivy-report-backend.txt + + Image-Vuln-Check-Frontend: + runs-on: ubuntu-24.04 + needs: [Build-App-Frontend] + continue-on-error: true + defaults: + run: + shell: bash + working-directory: frontend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Docker Image + run: docker build -t docker.io/${{ vars.DOCKERHUB_USERNAME }}/chatgpt-frontend:${{ github.sha }} . + - name: Run Trivy Vulnerability Scanner + uses: aquasecurity/trivy-action@0.20.0 + with: + image-ref: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/chatgpt-frontend:${{ github.sha }}" + format: "table" + exit-code: "1" + ignore-unfixed: true + output: trivy-report-frontend.txt + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: trivy-report-frontend + path: trivy-report-frontend.txt + + Push-To-DockerHub-Backend: + runs-on: ubuntu-24.04 + needs: [Image-Vuln-Check-Backend] + defaults: + run: + shell: bash + working-directory: backend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Building Docker Image + run: docker build . -t ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-backend:1.${{ github.run_number }}.${{ github.run_attempt}} + - name: Pushing Docker Image + run: docker push ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-backend:1.${{ github.run_number }}.${{ github.run_attempt}} + # - name: Build and push + # uses: docker/build-push-action@v6 + # with: + # file: backend/Dockerfile + # push: true + # tags: ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-backend:1.${{ github.run_number }}.${{ github.run_attempt}} + + Push-To-DockerHub-Frontend: + runs-on: ubuntu-24.04 + needs: [Image-Vuln-Check-Frontend] + defaults: + run: + shell: bash + working-directory: frontend/ + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Building Docker Image + run: docker build . -t ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-frontend:1.${{ github.run_number }}.${{ github.run_attempt}} + - name: Pushing Docker Image + run: docker push ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-frontend:1.${{ github.run_number }}.${{ github.run_attempt}} + # - name: Build and push + # uses: docker/build-push-action@v6 + # with: + # file: frontend/Dockerfile + # push: true + # tags: ${{ vars.DOCKERHUB_USERNAME }}/chatgpt-frontend:1.${{ github.run_number }}.${{ github.run_attempt}} + + Update-Helm-Chart-Backend: + runs-on: ubuntu-24.04 + needs: [Push-To-DockerHub-Backend] + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Update tag in Helm chart + run: | + sed -i 's/tag: .*/tag: "1.${{ github.run_number }}.${{ github.run_attempt}}"/' helm/charts/chatgpt-backend/values.yaml + - name: Update tag in K8s Deployment + run: | + sed -i 's|\(uj5ghare/chatgpt-backend:\)[^[:space:]]*|\1"1.${{ github.run_number }}.${{ github.run_attempt}}"|' k8s/manifests/backend/deployment.yml + - name: Commit and push changes + run: | + git pull origin ${{ github.ref_name }} + git config --global user.email "${{ secrets.GH_USER_MAIL }}" + git config --global user.name "${{ vars.GH_USER_NAME }}" + git add . + git commit -m "[helm] Updated tag in chatgpt-backend/values.yaml && [k8s] updated backend deployment image tag" + git push + + Update-Helm-Chart-Frontend: + runs-on: ubuntu-24.04 + needs: [Push-To-DockerHub-Frontend] + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Update tag in Helm chart + run: | + sed -i 's/tag: .*/tag: "1.${{ github.run_number }}.${{ github.run_attempt}}"/' helm/charts/chatgpt-frontend/values.yaml + - name: Update tag in K8s Deployment + run: | + sed -i 's|\(uj5ghare/chatgpt-frontend:\)[^[:space:]]*|\1"1.${{ github.run_number }}.${{ github.run_attempt}}"|' k8s/manifests/frontend/deployment.yml + - name: Commit and push changes + run: | + git pull origin ${{ github.ref_name }} + git config --global user.email "${{ secrets.GH_USER_MAIL }}" + git config --global user.name "${{ vars.GH_USER_NAME }}" + git add . + git commit -m "[helm] Updated tag in chatgpt-frontend/values.yaml && [k8s] updated frontend deployment image tag" + git push \ No newline at end of file diff --git a/.github/workflows/tf.yml b/.github/workflows/tf.yml new file mode 100644 index 000000000..89606a3b4 --- /dev/null +++ b/.github/workflows/tf.yml @@ -0,0 +1,70 @@ +name: Terraform Deployment Workflow + +on: + push: + branches: + - ujwal-cicd + - ujwal-tf + paths: + - ".github/workflows/tf.yml" + - "tf/files/**" + pull_request: + types: [synchronize] + workflow_dispatch: + +defaults: + run: + shell: bash + working-directory: tf/files/ + +permissions: + actions: read + contents: read + pull-requests: write + +env: + AWS_REGION: ${{ vars.AWS_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + +jobs: + Terraform-Deployment: + runs-on: ubuntu-24.04 + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "1.9.8" + + - name: Terraform fmt + id: fmt + run: terraform fmt -check + continue-on-error: true + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Validate + id: validate + run: terraform validate + + - name: Terraform Plan + id: plan + run: terraform plan -var-file=variables.tfvars -out=tfplan + continue-on-error: true + + - name: Terraform Apply + if: "contains(github.event.head_commit.message, 'Apply')" + id: apply + run: terraform apply -var-file=variables.tfvars --auto-approve + + - name: Terraform Destroy + if: "contains(github.event.head_commit.message, 'Destroy')" + id: destroy + run: terraform destroy -var-file=variables.tfvars --auto-approve diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 000000000..f97a514ce --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,31 @@ +# Python cache files +*.pyc +*.pyo +*.pyd +__pycache__ + +# Django database files +db.sqlite3 + +# IDE configurations +.idea +.vscode + +# Git files +.git +.gitignore + +# Logs and temporary files +*.log +*.tmp +*.bak + +# Virtualenv +venv/ +env/ +.venv + +# System files +.DS_Store +Thumbs.db + diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index 2cffd6649..000000000 --- a/backend/.env.example +++ /dev/null @@ -1,12 +0,0 @@ -FRONTEND_URL=http://127.0.0.1:3000 -BACKEND_URL=http://127.0.0.1:8000 - -BE_ADMIN_EMAIL=admin@admin.com -BE_ADMIN_PASSWORD=admin - -DJANGO_SECRET_KEY=... - -OPENAI_API_TYPE=... -OPENAI_API_BASE=... -OPENAI_API_VERSION=... -OPENAI_API_KEY=... diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 000000000..3fe17c34e --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.11-alpine +MAINTAINER ujwal.pachghare +WORKDIR /usr/bin/app +ENV PIP_NO_CACHE_DIR=1 \ + FRONTEND_URL="http://127.0.0.1:3000" \ + BACKEND_URL="http://127.0.0.1:8000" \ + BE_ADMIN_EMAIL="admin@admin.com" \ + BE_ADMIN_PASSWORD="admin" \ + DJANGO_SECRET_KEY="YWRtaW4=" +RUN rm -rf /var/lib/apt/lists/* +ADD dependencies.txt . +RUN pip install --upgrade pip \ + && pip install --no-cache-dir -r dependencies.txt +COPY . . +RUN python manage.py collectstatic --no-input +RUN python manage.py migrate +EXPOSE 8000 +CMD ["gunicorn","backend.wsgi:application","--bind","0.0.0.0:8000","--reload"] diff --git a/backend/dependencies.txt b/backend/dependencies.txt index 2363ba87e..ac22cc300 100644 --- a/backend/dependencies.txt +++ b/backend/dependencies.txt @@ -50,3 +50,4 @@ urllib3==2.0.5 uvicorn==0.27.1 virtualenv==20.24.5 yarl==1.9.2 +gunicorn==23.0.0 diff --git a/db/db_backup.sh b/db/db_backup.sh new file mode 100644 index 000000000..85d64bc1f --- /dev/null +++ b/db/db_backup.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Configuration +DB_PATH="../backend/db.sqlite3" +BACKUP_DIR="./backups" +DATE=$(date +%Y-%m-%d_%H-%M-%S) + +# Make the backup directory if it doesn't exist +sudo mkdir -p $BACKUP_DIR + +# Backup the SQLite DB by copying it to the backup folder with a timestamp +sudo cp $DB_PATH $BACKUP_DIR/db_backup_$DATE.sqlite3 + +# Optionally, clean up backups older than 7 days +sudo find $BACKUP_DIR -type f -name "*.sqlite3" -mtime +7 -exec rm -f {} \; + +echo "Backup completed: $BACKUP_DIR/db_backup_$DATE.sqlite3" + +# Crontab Configuration +# chmod +x db_backup.sh +# crontab -e +# 0 3 * * * db_backup.sh - running at 3AM everyday diff --git a/db/db_restore.sh b/db/db_restore.sh new file mode 100644 index 000000000..133cf6c98 --- /dev/null +++ b/db/db_restore.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Configuration +DB_PATH="../backend/db.sqlite3" +BACKUP_DIR="./backups" + +# Check if a backup file was provided +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +BACKUP_FILE="$1" + +# Check if the backup file exists +if [ ! -f "$BACKUP_FILE" ]; then + echo "Error: Backup file '$BACKUP_FILE' not found." + exit 1 +fi + +# Restore the database +cp "$BACKUP_FILE" "$DB_PATH" +echo "Database restored from '$BACKUP_FILE' to '$DB_PATH'." + +# Run migrations (optional) +cd ../backend/ +python manage.py migrate + +# chmod +x db_restore.sh diff --git a/db/init.sql b/db/init.sql new file mode 100644 index 000000000..d92611324 --- /dev/null +++ b/db/init.sql @@ -0,0 +1,41 @@ +-- Table: Role +CREATE TABLE IF NOT EXISTS Role ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(20) NOT NULL +); + +-- Table: Version +CREATE TABLE IF NOT EXISTS Version ( + id UUID PRIMARY KEY, + root_massage_id UUID, + conversion_id UUID, + parent_verion_id UUID, + + FOREIGN KEY (root_massage_id) REFERENCES Message(id), + FOREIGN KEY (conversion_id) REFERENCES Conversation(id), + FOREIGN KEY (parent_verion_id) REFERENCES Version(id) +); + +-- Table: Conversation +CREATE TABLE IF NOT EXISTS Conversation ( + id UUID PRIMARY KEY, + title VARCHAR(100) NOT NULL, + created_at DATETIME, + modified_at DATETIME, + active_version_id UUID, + active BOOLEAN DEFAULT FALSE, + + FOREIGN KEY (active_version_id) REFERENCES Version(id) +); + +-- Table: Message +CREATE TABLE IF NOT EXISTS Message ( + id UUID PRIMARY KEY, + content_text NOT NULL, + role_id NOT NULL, + created_at DATETIME, + version_id UUID NOT NULL, + + FOREIGN KEY (role_id) REFERENCES Role(id), + FOREIGN KEY (version_id) REFERENCES Version(id) +); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..f451ca879 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +version: "3.8" + +services: + + frontend: + restart: always + build: + context: ./frontend/ + dockerfile: Dockerfile + image: uj5ghare/chatgpt-frontend:latest + container_name: frontend-con + ports: + - "3000:3000" + env_file: + - ./frontend/.env.local + volumes: + - frontend:/usr/bin/app + networks: + - two-tier + depends_on: + - backend + + backend: + restart: always + build: + context: ./backend/ + dockerfile: Dockerfile + image: uj5ghare/chatgpt-backend:latest + container_name: backend-con + ports: + - "8000:8000" + env_file: + - ./backend/.env + volumes: + - backend:/usr/bin/app + networks: + - two-tier + +volumes: + frontend: + driver: local + backend: + driver: local + +networks: + two-tier: + driver: bridge diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 000000000..f97a514ce --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,31 @@ +# Python cache files +*.pyc +*.pyo +*.pyd +__pycache__ + +# Django database files +db.sqlite3 + +# IDE configurations +.idea +.vscode + +# Git files +.git +.gitignore + +# Logs and temporary files +*.log +*.tmp +*.bak + +# Virtualenv +venv/ +env/ +.venv + +# System files +.DS_Store +Thumbs.db + diff --git a/frontend/.env.local.example b/frontend/.env.local.example deleted file mode 100644 index b1edae03b..000000000 --- a/frontend/.env.local.example +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8000 diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..ca3331721 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,26 @@ +FROM node:18.20.4-slim AS base +LABEL stage="install" +WORKDIR /install +COPY package.json . +RUN npm install -g npm@10.9.0 +RUN npm install + + +FROM node:18.20.4-slim AS build +LABEL stage="build" +WORKDIR /build +COPY --from=base /install/node_modules ./node_modules +COPY . . +RUN npm install -g npm@10.9.0 +RUN npm run build + +FROM node:18.20-alpine +LABEL stage="deploy" +MAINTAINER ujwal.pachghare +WORKDIR /usr/bin/app +ENV NEXT_PUBLIC_API_BASE_URL="http://127.0.0.1:8000" +COPY --from=build /build/node_modules ./node_modules +COPY --from=build /build/.next ./.next +COPY . . +EXPOSE 3000 +CMD ["npm","start"] diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 253dd797c..000000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,1201 +0,0 @@ -{ - "name": "frontend", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "0.1.0", - "dependencies": { - "@reduxjs/toolkit": "^1.9.5", - "axios": "^1.6.0", - "js-cookie": "^3.0.5", - "next": "latest", - "openai": "^4.7.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-modal": "^3.16.1", - "react-redux": "^8.1.2", - "react-syntax-highlighter": "^15.5.0", - "redux-thunk": "^2.4.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@next/env": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", - "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", - "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", - "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", - "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", - "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", - "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", - "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", - "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", - "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", - "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reduxjs/toolkit": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", - "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", - "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", - "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "node_modules/@types/node": { - "version": "18.19.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.18.tgz", - "integrity": "sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" - }, - "node_modules/@types/react": { - "version": "18.2.58", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.58.tgz", - "integrity": "sha512-TaGvMNhxvG2Q0K0aYxiKfNDS5m5ZsoIBBbtfUorxdH4NGSXIlYvZxLJI+9Dd3KjeB3780bciLyAb7ylO8pLhPw==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, - "node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001589", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz", - "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/digest-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", - "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", - "dependencies": { - "base-64": "^0.1.0", - "md5": "^2.3.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" - }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "engines": { - "node": "*" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "engines": { - "node": ">=14" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", - "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", - "dependencies": { - "@next/env": "14.1.0", - "@swc/helpers": "0.5.2", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", - "postcss": "8.4.31", - "styled-jsx": "5.1.1" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=18.17.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "14.1.0", - "@next/swc-darwin-x64": "14.1.0", - "@next/swc-linux-arm64-gnu": "14.1.0", - "@next/swc-linux-arm64-musl": "14.1.0", - "@next/swc-linux-x64-gnu": "14.1.0", - "@next/swc-linux-x64-musl": "14.1.0", - "@next/swc-win32-arm64-msvc": "14.1.0", - "@next/swc-win32-ia32-msvc": "14.1.0", - "@next/swc-win32-x64-msvc": "14.1.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/openai": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.28.0.tgz", - "integrity": "sha512-JM8fhcpmpGN0vrUwGquYIzdcEQHtFuom6sRCbbCM6CfzZXNuRk33G7KfeRAIfnaCxSpzrP5iHtwJzIm6biUZ2Q==", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - }, - "bin": { - "openai": "bin/cli" - } - }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-modal": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", - "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", - "dependencies": { - "exenv": "^1.2.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.0", - "warning": "^4.0.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", - "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" - } - }, - "node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/react-syntax-highlighter": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", - "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", - "dependencies": { - "@babel/runtime": "^7.3.1", - "highlight.js": "^10.4.1", - "lowlight": "^1.17.0", - "prismjs": "^1.27.0", - "refractor": "^3.6.0" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "peerDependencies": { - "redux": "^4" - } - }, - "node_modules/refractor": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", - "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", - "dependencies": { - "hastscript": "^6.0.0", - "parse-entities": "^2.0.0", - "prismjs": "~1.27.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/prismjs": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", - "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/helm/charts/chatgpt-backend/.helmignore b/helm/charts/chatgpt-backend/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/helm/charts/chatgpt-backend/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/charts/chatgpt-backend/Chart.yaml b/helm/charts/chatgpt-backend/Chart.yaml new file mode 100644 index 000000000..3158e587b --- /dev/null +++ b/helm/charts/chatgpt-backend/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: chatgpt-backend +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm/charts/chatgpt-backend/templates/NOTES.txt b/helm/charts/chatgpt-backend/templates/NOTES.txt new file mode 100644 index 000000000..d61872299 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "chatgpt-backend.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "chatgpt-backend.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "chatgpt-backend.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "chatgpt-backend.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm/charts/chatgpt-backend/templates/_helpers.tpl b/helm/charts/chatgpt-backend/templates/_helpers.tpl new file mode 100644 index 000000000..311bc4bbb --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "chatgpt-backend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chatgpt-backend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chatgpt-backend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "chatgpt-backend.labels" -}} +helm.sh/chart: {{ include "chatgpt-backend.chart" . }} +{{ include "chatgpt-backend.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chatgpt-backend.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chatgpt-backend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "chatgpt-backend.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "chatgpt-backend.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/charts/chatgpt-backend/templates/deployment.yml b/helm/charts/chatgpt-backend/templates/deployment.yml new file mode 100644 index 000000000..67e570229 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/deployment.yml @@ -0,0 +1,29 @@ +# This is a sample deployment manifest file for a simple web application. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.defaultLabel }}-deployment + namespace: {{ .Values.defaultNamespace }} + labels: + app: {{ .Values.defaultLabel }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 25% + selector: + matchLabels: + app: {{ .Values.defaultLabel }} + template: + metadata: + labels: + app: {{ .Values.defaultLabel }} + spec: + containers: + - name: {{ .Values.defaultLabel }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + ports: + - containerPort: {{ .Values.service.targetPort }} + imagePullPolicy: {{ .Values.image.pullPolicy}} diff --git a/helm/charts/chatgpt-backend/templates/hpa.yml b/helm/charts/chatgpt-backend/templates/hpa.yml new file mode 100644 index 000000000..b33fff8c7 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/hpa.yml @@ -0,0 +1,28 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Values.defaultLabel }}-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Values.defaultLabel }}-deployment + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} diff --git a/helm/charts/chatgpt-backend/templates/pv.yml b/helm/charts/chatgpt-backend/templates/pv.yml new file mode 100644 index 000000000..736001f30 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/pv.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ .Values.defaultLabel }}-pv + namespace: {{ .Values.defaultNamespace }} +spec: + capacity: + storage: 1Gi + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + hostPath: + path: /data/db diff --git a/helm/charts/chatgpt-backend/templates/pvc.yml b/helm/charts/chatgpt-backend/templates/pvc.yml new file mode 100644 index 000000000..331925dc9 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/pvc.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.defaultLabel }}-pvc + namespace: {{ .Values.defaultNamespace }} +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: 1G diff --git a/helm/charts/chatgpt-backend/templates/service.yml b/helm/charts/chatgpt-backend/templates/service.yml new file mode 100644 index 000000000..cc3b55257 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/service.yml @@ -0,0 +1,16 @@ +# Service for the application +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.defaultLabel }}-svc + namespace: {{ .Values.defaultNamespace }} + labels: + app: {{ .Values.defaultLabel }} +spec: + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort}} + protocol: TCP + selector: + app: {{ .Values.defaultLabel }} + type: {{ .Values.service.type }} diff --git a/helm/charts/chatgpt-backend/templates/tests/test-connection.yaml b/helm/charts/chatgpt-backend/templates/tests/test-connection.yaml new file mode 100644 index 000000000..f14e19243 --- /dev/null +++ b/helm/charts/chatgpt-backend/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "chatgpt-backend.fullname" . }}-test-connection" + labels: + {{- include "chatgpt-backend.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "chatgpt-backend.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm/charts/chatgpt-backend/values.yaml b/helm/charts/chatgpt-backend/values.yaml new file mode 100644 index 000000000..ea7e5b03c --- /dev/null +++ b/helm/charts/chatgpt-backend/values.yaml @@ -0,0 +1,28 @@ +# Default values for chatgpt-backend-chart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 2 +defaultLabel: chatgpt-backend +defaultNamespace: chatgpt + +image: + repository: uj5ghare/chatgpt-backend + pullPolicy: Always + tag: "1.39.1" + +service: + type: ClusterIP + port: 80 + targetPort: 8000 + +ingress: + className: nginx + host: chatgpt-backend.local + +autoscaling: + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + diff --git a/helm/charts/chatgpt-frontend/.helmignore b/helm/charts/chatgpt-frontend/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/helm/charts/chatgpt-frontend/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/charts/chatgpt-frontend/Chart.yaml b/helm/charts/chatgpt-frontend/Chart.yaml new file mode 100644 index 000000000..1b90dcb37 --- /dev/null +++ b/helm/charts/chatgpt-frontend/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: chatgpt-frontend +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm/charts/chatgpt-frontend/templates/NOTES.txt b/helm/charts/chatgpt-frontend/templates/NOTES.txt new file mode 100644 index 000000000..e83bafb5a --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "chatgpt-frontend.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "chatgpt-frontend.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "chatgpt-frontend.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "chatgpt-frontend.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm/charts/chatgpt-frontend/templates/_helpers.tpl b/helm/charts/chatgpt-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..8ae6395b5 --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "chatgpt-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chatgpt-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chatgpt-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "chatgpt-frontend.labels" -}} +helm.sh/chart: {{ include "chatgpt-frontend.chart" . }} +{{ include "chatgpt-frontend.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chatgpt-frontend.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chatgpt-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "chatgpt-frontend.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "chatgpt-frontend.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/charts/chatgpt-frontend/templates/deployment.yml b/helm/charts/chatgpt-frontend/templates/deployment.yml new file mode 100644 index 000000000..67e570229 --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/deployment.yml @@ -0,0 +1,29 @@ +# This is a sample deployment manifest file for a simple web application. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.defaultLabel }}-deployment + namespace: {{ .Values.defaultNamespace }} + labels: + app: {{ .Values.defaultLabel }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 25% + selector: + matchLabels: + app: {{ .Values.defaultLabel }} + template: + metadata: + labels: + app: {{ .Values.defaultLabel }} + spec: + containers: + - name: {{ .Values.defaultLabel }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + ports: + - containerPort: {{ .Values.service.targetPort }} + imagePullPolicy: {{ .Values.image.pullPolicy}} diff --git a/helm/charts/chatgpt-frontend/templates/hpa.yml b/helm/charts/chatgpt-frontend/templates/hpa.yml new file mode 100644 index 000000000..b33fff8c7 --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/hpa.yml @@ -0,0 +1,28 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Values.defaultLabel }}-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Values.defaultLabel }}-deployment + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} diff --git a/helm/charts/chatgpt-frontend/templates/service.yml b/helm/charts/chatgpt-frontend/templates/service.yml new file mode 100644 index 000000000..cc3b55257 --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/service.yml @@ -0,0 +1,16 @@ +# Service for the application +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.defaultLabel }}-svc + namespace: {{ .Values.defaultNamespace }} + labels: + app: {{ .Values.defaultLabel }} +spec: + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort}} + protocol: TCP + selector: + app: {{ .Values.defaultLabel }} + type: {{ .Values.service.type }} diff --git a/helm/charts/chatgpt-frontend/templates/tests/test-connection.yaml b/helm/charts/chatgpt-frontend/templates/tests/test-connection.yaml new file mode 100644 index 000000000..48b15e863 --- /dev/null +++ b/helm/charts/chatgpt-frontend/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "chatgpt-frontend.fullname" . }}-test-connection" + labels: + {{- include "chatgpt-frontend.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "chatgpt-frontend.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm/charts/chatgpt-frontend/values.yaml b/helm/charts/chatgpt-frontend/values.yaml new file mode 100644 index 000000000..e3472ec95 --- /dev/null +++ b/helm/charts/chatgpt-frontend/values.yaml @@ -0,0 +1,26 @@ +# Default values for chatgpt-frontend-chart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 2 +defaultLabel: chatgpt-frontend + +image: + repository: uj5ghare/chatgpt-frontend + pullPolicy: Always + tag: "1.39.1" + +service: + type: ClusterIP + port: 80 + targetPort: 3000 + +ingress: + className: nginx + host: chatgpt-frontend.local + +autoscaling: + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 diff --git a/k8s/manifests/backend/deployment.yml b/k8s/manifests/backend/deployment.yml new file mode 100644 index 000000000..e3da54f50 --- /dev/null +++ b/k8s/manifests/backend/deployment.yml @@ -0,0 +1,29 @@ +# This is a sample deployment manifest file for a simple web application. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: chatgpt-backend-deployment + namespace: chatgpt + labels: + app: chatgpt-backend +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 25% + selector: + matchLabels: + app: chatgpt-backend + template: + metadata: + labels: + app: chatgpt-backend + spec: + containers: + - name: chatgpt-backend + image: uj5ghare/chatgpt-backend:"1.39.1" + ports: + - containerPort: 8000 + imagePullPolicy: Always diff --git a/k8s/manifests/backend/hpa.yml b/k8s/manifests/backend/hpa.yml new file mode 100644 index 000000000..d37930747 --- /dev/null +++ b/k8s/manifests/backend/hpa.yml @@ -0,0 +1,26 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: chatgpt-backend-hpa + namespace: chatgpt +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: chatgpt-backend-deployment + minReplicas: 1 + maxReplicas: 3 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + diff --git a/k8s/manifests/backend/pv.yml b/k8s/manifests/backend/pv.yml new file mode 100644 index 000000000..7bab91c2c --- /dev/null +++ b/k8s/manifests/backend/pv.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: chatgpt-backend-pv + namespace: chatgpt +spec: + capacity: + storage: 1Gi + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + hostPath: + path: /data/db diff --git a/k8s/manifests/backend/pvc.yml b/k8s/manifests/backend/pvc.yml new file mode 100644 index 000000000..5c22f5df2 --- /dev/null +++ b/k8s/manifests/backend/pvc.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: chatgpt-backend-pvc + namespace: chatgpt +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: 1G diff --git a/k8s/manifests/backend/service.yml b/k8s/manifests/backend/service.yml new file mode 100644 index 000000000..ad15f4dc3 --- /dev/null +++ b/k8s/manifests/backend/service.yml @@ -0,0 +1,16 @@ +# Service for the application +apiVersion: v1 +kind: Service +metadata: + name: chatgpt-backend-svc + namespace: chatgpt + labels: + app: chatgpt-backend +spec: + ports: + - port: 80 + targetPort: 8000 + protocol: TCP + selector: + app: chatgpt-backend + type: ClusterIP diff --git a/k8s/manifests/frontend/deployment.yml b/k8s/manifests/frontend/deployment.yml new file mode 100644 index 000000000..76709de8c --- /dev/null +++ b/k8s/manifests/frontend/deployment.yml @@ -0,0 +1,35 @@ +# This is a sample deployment manifest file for a simple web application. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: chatgpt-frontend-deployment + namespace: chatgpt + labels: + app: chatgpt-frontend +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 25% + selector: + matchLabels: + app: chatgpt-frontend + template: + metadata: + labels: + app: chatgpt-frontend + spec: + containers: + - name: chatgpt-frontend + image: uj5ghare/chatgpt-frontend:"1.39.1" + ports: + - containerPort: 3000 + imagePullPolicy: Always + env: + - name: NEXT_PUBLIC_API_BASE_URL + value: "http://127.0.0.1:8000" + # imagePullSecrets: + # - name: # pulling image from private registry with secrets + # cmd to create secrets = [ kubectl create secret docker-registry docker-secrets --docker-server=https://index.docker.io/v1/ --docker-username=ExamplaName --docker-password=ExamplePassword --docker-email=example@gmail.com ] diff --git a/k8s/manifests/frontend/hpa.yml b/k8s/manifests/frontend/hpa.yml new file mode 100644 index 000000000..76e3bb40e --- /dev/null +++ b/k8s/manifests/frontend/hpa.yml @@ -0,0 +1,26 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: chatgpt-frontend-hpa + namespace: chatgpt +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: chatgpt-frontend-deployment + minReplicas: 1 + maxReplicas: 3 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + diff --git a/k8s/manifests/frontend/service.yml b/k8s/manifests/frontend/service.yml new file mode 100644 index 000000000..77747266a --- /dev/null +++ b/k8s/manifests/frontend/service.yml @@ -0,0 +1,16 @@ +# Service for the application +apiVersion: v1 +kind: Service +metadata: + name: chatgpt-frontend-svc + namespace: chatgpt + labels: + app: chatgpt-frontend +spec: + ports: + - port: 80 + targetPort: 3000 + protocol: TCP + selector: + app: chatgpt-frontend + type: ClusterIP diff --git a/k8s/manifests/ingress.yml b/k8s/manifests/ingress.yml new file mode 100644 index 000000000..a963ef90e --- /dev/null +++ b/k8s/manifests/ingress.yml @@ -0,0 +1,28 @@ +# Ingress resource for the application +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: chatgpt-ing + namespace: chatgpt + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: chatgpt.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: chatgpt-frontend-svc + port: + number: 3000 + - path: /api + pathType: Prefix + backend: + service: + name: chatgpt-backend-svc + port: + number: 8000 diff --git a/nginx/backend b/nginx/backend new file mode 100644 index 000000000..82bfd8e96 --- /dev/null +++ b/nginx/backend @@ -0,0 +1,11 @@ +# Backend server configuration +server { + listen 90; + listen [::]:90; + + server_name _; + + location / { + proxy_pass http://127.0.0.1:8000; + } +} diff --git a/nginx/frontend b/nginx/frontend new file mode 100644 index 000000000..b4dc5e787 --- /dev/null +++ b/nginx/frontend @@ -0,0 +1,11 @@ +# Frontend server configuration +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + + location / { + proxy_pass http://127.0.0.1:3000; + } +} diff --git a/tf/files/backend.tf b/tf/files/backend.tf new file mode 100644 index 000000000..064f9eb43 --- /dev/null +++ b/tf/files/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "custom-chatgpt-project-bucket" + region = "ap-south-2" + key = "custom-chatgpt/terraform.tfstate" + dynamodb_table = "Lock-Files" + encrypt = true + } +} diff --git a/tf/files/ec2.tf b/tf/files/ec2.tf new file mode 100644 index 000000000..efb93fa68 --- /dev/null +++ b/tf/files/ec2.tf @@ -0,0 +1,30 @@ +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] # Canonical +} + +resource "aws_instance" "ec2" { + ami = data.aws_ami.ubuntu.id + instance_type = var.instance_type + key_name = var.key_name + vpc_security_group_ids = var.sg_id + root_block_device { + volume_size = 8 + } + user_data = templatefile("./setup.sh", {}) + + tags = { + Name = var.instance_name + } +} diff --git a/tf/files/provider.tf b/tf/files/provider.tf new file mode 100644 index 000000000..976071001 --- /dev/null +++ b/tf/files/provider.tf @@ -0,0 +1,14 @@ +terraform { + required_version = "~> 1.9.8" + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.76.0" + } + } +} + +provider "aws" { + region = var.region +} + diff --git a/tf/files/setup.sh b/tf/files/setup.sh new file mode 100644 index 000000000..aef49e30a --- /dev/null +++ b/tf/files/setup.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# For Ubuntu 22.04 + +# Setup Environment For Nextjs Application +sudo apt update -y +sudo apt install npm -y +sudo npm install -g n +sudo n 18.20.5 + +# Setup Enviroment For Django Application +sudo apt update -y +sudo apt install python3-venv -y +sudo python3 -m venv .venv +source /.venv/bin/activate diff --git a/tf/files/terraform.lock.hcl b/tf/files/terraform.lock.hcl new file mode 100644 index 000000000..e6f0f52f2 --- /dev/null +++ b/tf/files/terraform.lock.hcl @@ -0,0 +1,26 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.76.0" + constraints = "5.76.0" + hashes = [ + "h1:JSLR3JP9naVcnH0PHcDwwHr3aQB9vlW0+b8HQma1GpU=", + "zh:05b2a0d25fc07576f6698d4840d0d2ae2599484c49f1b911ea1154584557bc13", + "zh:1b22dd1d9c482739e133adb996a9c8b285ca7d978d0fe04deaa5588eba5d254c", + "zh:216088c8800e7b8d7eff7b1a822317bc6faec64f27946ffd22bb3494ac4175cb", + "zh:43e994112b1484bf49945c4885aa2fee32486c9a5d64b9146bbd6f309f24e332", + "zh:46a28ba800f176eef500f998217bccc331605ef05f11abb1728f727a81f3a8b0", + "zh:4fad2743174a600da76a0cceeec2fef8399a18d880ba8929d811cd5cea1b5dee", + "zh:5c42a2c1438cd7533456026f52b562715664490711fdea809f44610a7565c145", + "zh:792d4fd4be434682e4540d2579505c7f11f39d0efe1d12ee2761ed0d46c8cd51", + "zh:7bb5f9f87c9da6d62d6f89504f01a9d6d2f19dcaa0efc46ea51ebdc4bb6fd536", + "zh:81cdbd97f81b1110fce793944d5668a4389904979eb7d178d3142a6b0e175e5e", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ab4b881eb0f3812b702aaecf921c5c16bbcc33d61d668be4d72d6da9c57ded85", + "zh:c1d9d1166fd948845614deef81f3197568d0d3c2a03b8b97fff308ebc59043f9", + "zh:cda7530f2c01434e483d3faf62fc0685295e7f844176aa38df1ba65fa6a4407a", + "zh:fdad558b1c41aa68123d0da82cc0d65bc86d09eaa1ab1d3a167ec3bce0fc0c66", + ] +} + diff --git a/tf/files/variables.tf b/tf/files/variables.tf new file mode 100644 index 000000000..48aaa79b1 --- /dev/null +++ b/tf/files/variables.tf @@ -0,0 +1,9 @@ +variable "region" {} +variable "ami" {} +variable "instance_type" {} +variable "instance_name" {} +variable "key_name" {} +variable "sg_id" { + type = list(string) +} + diff --git a/tf/files/variables.tfvars b/tf/files/variables.tfvars new file mode 100644 index 000000000..32e343f6c --- /dev/null +++ b/tf/files/variables.tfvars @@ -0,0 +1,6 @@ +region = "ap-south-2" +ami = "ami-00322bc378c450310" +instance_type = "t3.micro" +instance_name = "custom-chatgpt" +key_name = "Ujwal-Hy" +sg_id = ["sg-0bcc288f12dfddea4"] \ No newline at end of file