diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..f4ec79e --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,39 @@ +name: Build, push image, and notify Watchtower +on: + push: + branches: + - main # Only triggers the build on the main branch + +jobs: + build-image: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: git.matthiasg.dev + username: ninluc + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: buildx-cache-${{ github.sha }} + restore-keys: | + buildx-cache- + + - name: Build and push Docker image + run: | + docker build -t git.matthiasg.dev/ninluc/smartwave-api:latest . + docker push git.matthiasg.dev/ninluc/smartwave-api:latest + + notify: + needs: build-image + runs-on: ubuntu-latest + steps: + - name: Notify Watchtower + run: "curl -i -X POST -H \"Authorization: Bearer ${{ secrets.WATCHTOWER_TOKEN }}\" https://watchtower.beermaker.matthiasg.dev/v1/update" \ No newline at end of file diff --git a/.gitignore b/.gitignore index c18dd8d..8c34a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__/ +orchestrateur/db.sqlite-shm diff --git a/cloud/Dockerfile b/cloud/Dockerfile new file mode 100644 index 0000000..5ce1cb7 --- /dev/null +++ b/cloud/Dockerfile @@ -0,0 +1,21 @@ +# Use a lightweight Python 3.11 image +FROM python:3.11-slim + +# Set the working directory inside the container +WORKDIR /cloud + +# Copy the requirements file and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code +COPY . . + +# Ensure the photo storage directory exists so the app doesn't crash on startup +RUN mkdir -p storage/dishPhotos + +# Expose the port the app will run on +EXPOSE 5000 + +# Use Gunicorn to run the application in production +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"] \ No newline at end of file diff --git a/cloud/app.py b/cloud/app.py index 87b4b21..e20f813 100644 --- a/cloud/app.py +++ b/cloud/app.py @@ -1,7 +1,94 @@ -from flask import Flask +import os +import base64 +import uuid +from flask import Flask, request, jsonify +from pymongo import MongoClient + +# Import your shared device types +from shared import deviceTypes app = Flask(__name__) +# --------------------------------------------------------- +# Configuration & Setup +# --------------------------------------------------------- + +# Configure MongoDB connection (adjust the URI as needed for your environment) +client = MongoClient("mongodb://localhost:27017/") +db = client["microwave_network_db"] +cooking_collection = db["cooking_parameters"] +device_network_collection = db["device_network"] + +# Ensure the photo storage directory exists when the app starts +PHOTO_DIR = "storage/dishPhotos" +os.makedirs(PHOTO_DIR, exist_ok=True) + +# --------------------------------------------------------- +# Routes +# --------------------------------------------------------- + @app.route("/") def hello_world(): - return "
Hello, World!
" \ No newline at end of file + return "Hello, World!
" + + +@app.route("/cooking-params", methods=["POST"]) +def cooking_params(): + data = request.get_json() + + if not data: + return jsonify({"error": "Invalid or missing JSON payload"}), 400 + + # 1. Handle the Photo + photo_b64 = data.get("photo") + if photo_b64: + # Generate a unique filename using UUID to avoid overwriting + filename = f"dish_{uuid.uuid4().hex}.jpg" + filepath = os.path.join(PHOTO_DIR, filename) + + try: + # Decode the base64 string and save it as a binary file + with open(filepath, "wb") as f: + f.write(base64.b64decode(photo_b64)) + + # Replace the giant base64 string in the dictionary with the local file path + # so we don't bloat the MongoDB document + data["photo"] = filepath + + except Exception as e: + return jsonify({"error": f"Failed to save photo: {str(e)}"}), 500 + + # 2. Save to MongoDB + try: + # Insert the dictionary directly into Mongo (it will retain your exact JSON keys) + cooking_collection.insert_one(data) + + # Remove the Mongo-injected '_id' object before returning the response + data.pop("_id", None) + return jsonify({"message": "Cooking parameters saved successfully", "data": data}), 201 + + except Exception as e: + return jsonify({"error": f"Database error: {str(e)}"}), 500 + + +@app.route("/device-network", methods=["POST"]) +def device_network(): + data = request.get_json() + + if not data: + return jsonify({"error": "Invalid or missing JSON payload"}), 400 + + + # 2. Save to MongoDB + try: + # Insert the dictionary directly into Mongo (it will retain your exact JSON keys) + device_network_collection.insert_one(data) + + return "", 200 + + except Exception as e: + return jsonify({"error": f"Database error: {str(e)}"}), 500 + + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/cloud/requirements.txt b/cloud/requirements.txt new file mode 100644 index 0000000..ab247cb --- /dev/null +++ b/cloud/requirements.txt @@ -0,0 +1,3 @@ +Flask==3.0.2 +pymongo==4.6.1 +gunicorn==21.2.0 \ No newline at end of file