diff --git a/.github/workflows/docker-buildx.yml b/.github/workflows/docker-buildx.yml index 9048b0b..02c9f50 100644 --- a/.github/workflows/docker-buildx.yml +++ b/.github/workflows/docker-buildx.yml @@ -14,9 +14,10 @@ # If the branch name is 'release/1.0.2-dev', the image is tagged as '1.0.2-dev'. # # * The 'latest' and 'stable' tags allow us to easily switch between different versions. -# * The 'dev' tag allows you to have a separate version for development. +# * The 'dev' tag allows you to have a separate version for development. -name: Build Meseeks Chat Docker Image + +name: Build and Push Docker Images on: workflow_dispatch: @@ -25,48 +26,96 @@ on: - "release/*" jobs: - build: + docker: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + - name: Extract version and release type + id: extract_version + run: | + BRANCH_NAME=${{ github.ref }} + VERSION=$(echo $BRANCH_NAME | cut -d'/' -f 3 | cut -d'-' -f 1) + RELEASE_TYPE=$(echo $BRANCH_NAME | cut -d'/' -f 3 | cut -d'-' -f 2) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "release_type=$RELEASE_TYPE" >> $GITHUB_OUTPUT + + - name: Docker meta for meeseeks-base + id: meta_base + uses: docker/metadata-action@v5 + with: + images: ghcr.io/bearlike/meeseeks-base + tags: | + type=raw,value=${{ steps.extract_version.outputs.version }}${{ steps.extract_version.outputs.release_type == 'dev' && '-dev' || '' }} + type=raw,value=latest,enable=${{ steps.extract_version.outputs.release_type == 'latest' }} + type=raw,value=stable,enable=${{ steps.extract_version.outputs.release_type == 'stable' }} + type=raw,value=dev,enable=${{ steps.extract_version.outputs.release_type == 'dev' }} + + - name: Docker meta for meeseeks-chat + id: meta_chat + uses: docker/metadata-action@v5 + with: + images: ghcr.io/bearlike/meeseeks-chat + tags: | + type=raw,value=${{ steps.extract_version.outputs.version }}${{ steps.extract_version.outputs.release_type == 'dev' && '-dev' || '' }} + type=raw,value=latest,enable=${{ steps.extract_version.outputs.release_type == 'latest' }} + type=raw,value=stable,enable=${{ steps.extract_version.outputs.release_type == 'stable' }} + type=raw,value=dev,enable=${{ steps.extract_version.outputs.release_type == 'dev' }} + + - name: Docker meta for meeseeks-api + id: meta_api + uses: docker/metadata-action@v5 + with: + images: ghcr.io/bearlike/meeseeks-api + tags: | + type=raw,value=${{ steps.extract_version.outputs.version }}${{ steps.extract_version.outputs.release_type == 'dev' && '-dev' || '' }} + type=raw,value=latest,enable=${{ steps.extract_version.outputs.release_type == 'latest' }} + type=raw,value=stable,enable=${{ steps.extract_version.outputs.release_type == 'stable' }} + type=raw,value=dev,enable=${{ steps.extract_version.outputs.release_type == 'dev' }} - - name: Login to DockerHub - uses: docker/login-action@v1 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 with: registry: ghcr.io - username: ${{ github.actor }} + username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract branch name - id: extract_branch - shell: bash - run: | - BRANCH_NAME=$(echo ${{ github.ref }} | sed 's/refs\/heads\///') - echo "branch=$BRANCH_NAME" >> $GITHUB_ENV - echo "Extracted branch name: $BRANCH_NAME" + - name: Build and push meeseeks-base + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile.base + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta_base.outputs.tags }} + labels: ${{ steps.meta_base.outputs.labels }} - - name: Set version and channel - id: version_channel - run: | - BRANCH_NAME=${{ env.branch }} - VERSION=$(echo $BRANCH_NAME | cut -d'/' -f 2 | cut -d'-' -f 1) - CHANNEL=$(echo $BRANCH_NAME | cut -d'/' -f 2 | cut -d'-' -f 2) - echo "version=$VERSION" >> $GITHUB_ENV - echo "channel=$CHANNEL" >> $GITHUB_ENV - echo "Extracted version: $VERSION" - echo "Extracted channel: $CHANNEL" - echo "Extracted branch name: $BRANCH_NAME" + - name: Build and push meeseeks-chat + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile.chat + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta_chat.outputs.tags }} + labels: ${{ steps.meta_chat.outputs.labels }} + build-args: | + BASE_IMAGE=ghcr.io/bearlike/meeseeks-base:${{ steps.extract_version.outputs.version }}${{ steps.extract_version.outputs.release_type == 'dev' && '-dev' || '' }} - - name: Build and push - uses: docker/build-push-action@v2 + - name: Build and push meeseeks-api + uses: docker/build-push-action@v5 with: context: . - push: true - tags: | - ghcr.io/bearlike/meeseeks-chat:${{ env.version }}${{ env.channel == 'dev' && '-dev' || '' }} - ghcr.io/bearlike/meeseeks-chat:${{ env.channel == 'latest' && 'latest' || env.channel == 'stable' && 'stable' || 'dev' }} + file: Dockerfile.api platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta_api.outputs.tags }} + labels: ${{ steps.meta_api.outputs.labels }} + build-args: | + BASE_IMAGE=ghcr.io/bearlike/meeseeks-base:${{ steps.extract_version.outputs.version }}${{ steps.extract_version.outputs.release_type == 'dev' && '-dev' || '' }} diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..33828ef --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1 + +# Dockerfile.api +ARG BASE_IMAGE="ghcr.io/bearlike/meeseeks-base:latest" +FROM $BASE_IMAGE + +ARG TITLE="Meeseeks API: Personal Assistant" + +LABEL title=$TITLE + +# Install the meeseeks-api dependencies +WORKDIR /app/meeseeks-api +RUN poetry install + +# Set API specific environment variable +ENV MASTER_API_TOKEN='msk-strong-password' + +# Expose port 5123 for the API +EXPOSE 5123 + +# Run the API application +ENTRYPOINT ["poetry", "run", "python", "backend.py"] diff --git a/Dockerfile b/Dockerfile.base similarity index 72% rename from Dockerfile rename to Dockerfile.base index 0a9fc6b..0581542 100644 --- a/Dockerfile +++ b/Dockerfile.base @@ -1,9 +1,10 @@ -# Dockerfile to build meeseeks-chat with core dependencies. +# syntax=docker/dockerfile:1 +# Dockerfile.base FROM python:3.11-buster # Set the title, GitHub repo URL, version, and author -ARG TITLE="Meeseeks Chat: Personal Assistant" \ +ARG TITLE="Meeseeks Base" \ VERSION="1.0.0" \ AUTHOR="Krishnakanth Alagiri" @@ -43,11 +44,7 @@ RUN pip install 'poetry>=1.8,<1.9' # Install the core dependencies RUN poetry install -# Install the meeseeks-chat dependencies -WORKDIR /app/meeseeks-chat -RUN poetry install - -# Set default environment variablesfor Meeseeks +# Set default environment variables for Meeseeks (common ones) ENV CACHE_DIR='/tmp/meeseeks_cache' \ DEFAULT_MODEL='gpt-3.5-turbo' \ LOG_LEVEL=DEBUG \ @@ -56,13 +53,3 @@ ENV CACHE_DIR='/tmp/meeseeks_cache' \ COLOREDLOGS_FIELD_STYLES='asctime=color=240;name=45,inverse' \ COLOREDLOGS_LEVEL_STYLES='info=220;spam=22;debug=34;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red' \ COLOREDLOGS_LOG_FORMAT='%(asctime)s [%(name)s] %(levelname)s %(message)s' - - -# Expose port 8501 for Streamlit -EXPOSE 8502 - -# Healthcheck to ensure the Streamlit server is running -HEALTHCHECK CMD curl --fail http://localhost:8502/_stcore/health - -# Run the Streamlit application -ENTRYPOINT ["poetry", "run", "python", "-m", "streamlit", "run", "chat_master.py", "--server.port=8502", "--server.address=0.0.0.0"] diff --git a/Dockerfile.chat b/Dockerfile.chat new file mode 100644 index 0000000..c0af248 --- /dev/null +++ b/Dockerfile.chat @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1 + +# Dockerfile.chat +ARG BASE_IMAGE="ghcr.io/bearlike/meeseeks-base:latest" +FROM $BASE_IMAGE + +ARG TITLE="Meeseeks Chat: Personal Assistant" + +LABEL title=$TITLE + +# Install the meeseeks-chat dependencies +WORKDIR /app/meeseeks-chat +RUN poetry install + +# Expose port 8502 for Streamlit +EXPOSE 8502 + +# Healthcheck to ensure the Streamlit server is running +HEALTHCHECK CMD curl --fail http://localhost:8502/_stcore/health + +# Run the Streamlit application +ENTRYPOINT ["poetry", "run", "python", "-m", "streamlit", "run", "chat_master.py", "--server.port=8502", "--server.address=0.0.0.0"] diff --git a/meeseeks-api/README.md b/meeseeks-api/README.md index a3bbbbd..e2c77a1 100644 --- a/meeseeks-api/README.md +++ b/meeseeks-api/README.md @@ -1,5 +1,6 @@ # meeseeks-api -REST API Engine wrapped around the meeseeks-core +- REST API Engine wrapped around the meeseeks-core. +- No components are explicitly tested for safety or security. Use with caution in a production environment. [Link to GitHub Repository](https://github.com/bearlike/Personal-Assistant/edit/main/README.md) diff --git a/meeseeks-api/backend.py b/meeseeks-api/backend.py index 8e83d74..61b4279 100644 --- a/meeseeks-api/backend.py +++ b/meeseeks-api/backend.py @@ -29,16 +29,28 @@ # Load environment variables load_dotenv() +# Get the API token from the environment variables +# The default API token is "msk-strong-password" +MASTER_API_TOKEN = os.getenv("MASTER_API_TOKEN", "msk-strong-password") # Initialize logger logging = get_logger(name="meeseeks-api") - +logging.debug("Starting API server with API token: %s", MASTER_API_TOKEN) # Create Flask application app = Flask(__name__) + +authorizations = { + 'apikey': { + 'type': 'apiKey', + 'in': 'header', + 'name': 'X-API-KEY' + } +} +VERSION = os.getenv("VERSION", "(Dev)") # Create API instance with Swagger documentation -api = Api(app, version='1.0', title='Meeseeks API', +api = Api(app, version=VERSION, title='Meeseeks API', description='Interact with Meeseeks through a REST API', - doc='/swagger-ui/') + doc='/swagger-ui/', authorizations=authorizations, security='apikey') # Define API namespace ns = api.namespace('api', description='Meeseeks operations') @@ -70,7 +82,7 @@ class MeeseeksQuery(Resource): action plan as a JSON response. """ - @api.doc(security='apiKey') + @api.doc(security='apikey') @api.expect(api.model('Query', {'query': fields.String( required=True, description='The user query')})) @api.response(200, 'Success', task_queue_model) @@ -84,10 +96,12 @@ def post(self) -> Dict: Requires a valid API token for authorization. """ # Get API token from headers - api_token = request.headers.get('X-API-Key') + api_token = request.headers.get('X-API-Key', None) # Validate API token - if api_token != os.getenv("MASTER_API_TOKEN"): + if api_token is None: + return {"message": "API token is not provided."}, 401 + if api_token != MASTER_API_TOKEN: logging.warning( "Unauthorized API call attempt with token: %s", api_token) return {"message": "Unauthorized"}, 401 @@ -111,4 +125,4 @@ def post(self) -> Dict: if __name__ == '__main__': - app.run(debug=True) + app.run(debug=True, host='0.0.0.0', port=5123)