From 47b15f2120facccac5bfd655088cc008ec6a7275 Mon Sep 17 00:00:00 2001 From: Marco de Jongh Date: Sun, 10 Aug 2025 17:29:51 +1000 Subject: [PATCH 1/3] Add docker container for board-controller --- board-controller/.dockerignore | 43 ++++++ board-controller/Dockerfile | 31 ++++ board-controller/README.md | 216 +++++++++++++++++++++------- board-controller/docker-compose.yml | 40 ++++++ 4 files changed, 276 insertions(+), 54 deletions(-) create mode 100644 board-controller/.dockerignore create mode 100644 board-controller/Dockerfile create mode 100644 board-controller/docker-compose.yml diff --git a/board-controller/.dockerignore b/board-controller/.dockerignore new file mode 100644 index 00000000..20b93024 --- /dev/null +++ b/board-controller/.dockerignore @@ -0,0 +1,43 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Database (will be created in container) +board_controller.db +*.db + +# Logs +*.log + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Docker +.dockerignore +Dockerfile +docker-compose.yml + +# Git +.git/ +.gitignore + +# Static frontend files (we redirect to BoardSesh now) +static/ + +# Data directory (mounted as volume) +data/ \ No newline at end of file diff --git a/board-controller/Dockerfile b/board-controller/Dockerfile new file mode 100644 index 00000000..85bbeddf --- /dev/null +++ b/board-controller/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.11-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + bluez \ + python3-dbus \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY main.py . +COPY controller.py . + +# Create data directory for persistent storage +RUN mkdir -p /app/data + +# Expose port +EXPOSE 8000 + +# Set environment variables +ENV DB_PATH=/app/data/board_controller.db +ENV PYTHONUNBUFFERED=1 + +# Run the application +CMD ["python", "main.py", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/board-controller/README.md b/board-controller/README.md index 66c6c78e..d3299a9b 100644 --- a/board-controller/README.md +++ b/board-controller/README.md @@ -1,35 +1,34 @@ # Board Controller -A unified Python server that combines Bluetooth control for Kilter/Tension boards with WebSocket integration for BoardSesh.com queue management. +A Python WebSocket server that enables persistent queue management and collaborative control for Kilter/Tension climbing boards. Integrates with BoardSesh.com to provide synchronized queue state across multiple devices and apps. ## Features - **Single Entry Point**: One command starts everything -- **Bluetooth Support**: Accepts commands from Kilter/Tension mobile apps -- **Web Integration**: Seamlessly integrates with BoardSesh.com via WebSocket -- **Queue Persistence**: SQLite database stores queue state across restarts -- **QR Code Interface**: Simple web UI with QR code for easy connection -- **API Caching**: BoardSesh API responses cached locally for performance +- **Bluetooth Support**: Accepts commands from Kilter/Tension mobile apps (optional) +- **WebSocket Integration**: Real-time synchronization with BoardSesh.com +- **Queue Persistence**: SQLite database maintains queue state across restarts +- **Auto-redirect**: localhost:8000 automatically redirects to BoardSesh with controller integration +- **Multi-device Support**: Control queue from both web browser and mobile apps simultaneously ## Quick Start ### Prerequisites - Python 3.8+ -- Node.js 18+ (for building frontend) -- Bluetooth adapter (for Bluetooth functionality) +- Bluetooth adapter (optional, for Bluetooth functionality) ### Installation ```bash # Install Python dependencies -pip install -r requirements.txt +pip install fastapi uvicorn aiosqlite -# Build React frontend -cd frontend -chmod +x build.sh -./build.sh -cd .. +# Or create requirements.txt: +echo "fastapi>=0.100.0" > requirements.txt +echo "uvicorn>=0.23.0" >> requirements.txt +echo "aiosqlite>=0.19.0" >> requirements.txt +pip install -r requirements.txt ``` ### Usage @@ -49,20 +48,18 @@ python main.py --port 8080 1. Run `python main.py` 2. Open http://localhost:8000 in your browser -3. Scan the QR code with your phone -4. This opens BoardSesh with controller integration -5. The controller takes over queue management -6. Use both web interface and Kilter/Tension apps +3. You'll be automatically redirected to BoardSesh with controller integration +4. The controller takes over queue management from BoardSesh +5. Add climbs to the queue - they persist across browser refreshes +6. Use both BoardSesh web interface and Kilter/Tension mobile apps ## Architecture ### Components -- **main.py**: Unified server entry point -- **bluetooth_controller.py**: Bluetooth service wrapper -- **boardsesh_client.py**: API client with caching -- **frontend/**: React web interface -- **board_controller.db**: SQLite database (auto-created) +- **main.py**: Unified FastAPI server with WebSocket support +- **controller.py**: Original Bluetooth controller integration (optional) +- **board_controller.db**: SQLite database for queue persistence (auto-created) ### API Endpoints @@ -75,49 +72,53 @@ python main.py --port 8080 ### WebSocket Protocol -Messages sent between BoardSesh and controller: +Key message types between BoardSesh and controller: -```typescript -// Handshake +```json +// Initial handshake from controller { "type": "controller-handshake", "sessionId": "uuid", + "controllerId": "uuid", "capabilities": ["queue", "bluetooth", "persistence"] } +// New connection request (triggers queue data response) +{ + "type": "new-connection", + "source": "boardsesh-client" +} + +// Queue state update +{ + "type": "initial-queue-data", + "queue": [...], + "currentClimbQueueItem": {...} +} + // Queue operations { "type": "add-queue-item", - "item": { /* queue item */ } + "item": { "climb": {...}, "addedBy": "user", "uuid": "..." } } -// Bluetooth updates { - "type": "bluetooth-update", - "ledData": [...], - "inferredClimb": "climb-uuid" + "type": "update-current-climb", + "item": {...}, + "shouldAddToQueue": false } ``` ## Development -### Frontend Development - -```bash -cd frontend -npm run dev # Starts Vite dev server on port 3001 -``` - -### Python Development - The server uses FastAPI with automatic reload: ```bash -# Install in development mode -pip install -e . +# Run with auto-reload for development +python main.py --no-bluetooth -# Run with auto-reload -uvicorn main:app --reload --port 8000 +# Or use uvicorn directly +uvicorn main:BoardController().app --reload --port 8000 ``` ### Database @@ -184,31 +185,80 @@ Options: ```bash # Install system dependencies sudo apt update -sudo apt install python3-pip bluez python3-dbus +sudo apt install python3-pip python3-venv + +# Optional: for Bluetooth support +sudo apt install bluez python3-dbus # Clone and setup git clone cd board-controller -pip install -r requirements.txt -cd frontend && ./build.sh && cd .. +python3 -m venv venv +source venv/bin/activate +pip install fastapi uvicorn aiosqlite + +# Run +python main.py + +# Run as service (create systemd service file) +sudo tee /etc/systemd/system/board-controller.service > /dev/null < Date: Sun, 10 Aug 2025 18:28:19 +1000 Subject: [PATCH 2/3] Add SSL to websocket server --- .github/workflows/board-controller-docker.yml | 72 ++++++++ .../hooks/use-controller-websocket.ts | 10 +- board-controller/Dockerfile | 5 +- board-controller/README.md | 32 +++- board-controller/generate_cert.py | 121 ++++++++++++++ board-controller/main.py | 158 ++++++++++++++++-- board-controller/requirements.txt | 5 +- 7 files changed, 387 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/board-controller-docker.yml create mode 100644 board-controller/generate_cert.py diff --git a/.github/workflows/board-controller-docker.yml b/.github/workflows/board-controller-docker.yml new file mode 100644 index 00000000..4d015745 --- /dev/null +++ b/.github/workflows/board-controller-docker.yml @@ -0,0 +1,72 @@ +name: Build and Publish Board Controller Docker Image + +on: + push: + branches: [ main ] + paths: + - 'board-controller/**' + - '.github/workflows/board-controller-docker.yml' + pull_request: + branches: [ main ] + paths: + - 'board-controller/**' + - '.github/workflows/board-controller-docker.yml' + release: + types: [published] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: boardsesh-board-controller + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./board-controller + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + if: github.event_name != 'pull_request' + with: + subject-name: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.build.outputs.digest }} + push-to-registry: true \ No newline at end of file diff --git a/app/components/queue-control/hooks/use-controller-websocket.ts b/app/components/queue-control/hooks/use-controller-websocket.ts index 9fd2567c..e7f040b5 100644 --- a/app/components/queue-control/hooks/use-controller-websocket.ts +++ b/app/components/queue-control/hooks/use-controller-websocket.ts @@ -74,7 +74,13 @@ export const useControllerWebSocket = (): UseControllerWebSocketReturn => { } // Ensure the WebSocket URL includes the /ws path - const wsUrl = controllerUrl.endsWith('/ws') ? controllerUrl : `${controllerUrl}/ws`; + let wsUrl = controllerUrl.endsWith('/ws') ? controllerUrl : `${controllerUrl}/ws`; + + // Use WSS if the page is loaded over HTTPS + if (typeof window !== 'undefined' && window.location.protocol === 'https:') { + wsUrl = wsUrl.replace(/^ws:\/\//, 'wss://'); + } + console.log('🎮 Connecting to Board Controller:', wsUrl); wsRef.current = new WebSocket(wsUrl); @@ -175,7 +181,7 @@ export const useControllerWebSocket = (): UseControllerWebSocketReturn => { wsRef.current.close(1000, 'Component unmounting'); } }; - }, [isControllerMode, connect]); + }, [isControllerMode, controllerUrl, connect]); return { isControllerMode, diff --git a/board-controller/Dockerfile b/board-controller/Dockerfile index 85bbeddf..850bce55 100644 --- a/board-controller/Dockerfile +++ b/board-controller/Dockerfile @@ -26,6 +26,7 @@ EXPOSE 8000 # Set environment variables ENV DB_PATH=/app/data/board_controller.db ENV PYTHONUNBUFFERED=1 +ENV DOCKER_CONTAINER=1 -# Run the application -CMD ["python", "main.py", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +# Run the application with auto-SSL enabled +CMD ["python", "main.py", "--host", "0.0.0.0", "--port", "8000", "--auto-ssl"] \ No newline at end of file diff --git a/board-controller/README.md b/board-controller/README.md index d3299a9b..2579d841 100644 --- a/board-controller/README.md +++ b/board-controller/README.md @@ -146,6 +146,36 @@ Options: - `--no-bluetooth` - Disable Bluetooth support - `--port PORT` - Server port (default: 8000) - `--host HOST` - Server host (default: 0.0.0.0) +- `--ssl-cert FILE` - SSL certificate file for HTTPS/WSS +- `--ssl-key FILE` - SSL private key file for HTTPS/WSS + +### SSL Setup (HTTPS/WSS Support) + +When accessing the controller from HTTPS sites like boardsesh.com, you need SSL support: + +**Generate development certificates:** +```bash +# Install cryptography library +pip install cryptography + +# Generate self-signed certificate +python generate_cert.py --ip 192.168.1.112 + +# Start server with SSL +python main.py --ssl-cert server.crt --ssl-key server.key +``` + +**Production certificates:** +```bash +# Use Let's Encrypt or other CA +certbot certonly --standalone -d your-domain.com + +# Start with real certificates +python main.py --ssl-cert /etc/letsencrypt/live/your-domain.com/fullchain.pem \ + --ssl-key /etc/letsencrypt/live/your-domain.com/privkey.pem +``` + +The server automatically detects SSL and uses WSS for WebSocket connections. ## Troubleshooting @@ -170,7 +200,7 @@ Options: 1. Check firewall settings 2. Ensure port 8000 is accessible -3. For HTTPS sites, controller needs HTTPS/WSS too +3. For HTTPS sites, controller needs HTTPS/WSS too (see SSL Setup below) ### Database Issues diff --git a/board-controller/generate_cert.py b/board-controller/generate_cert.py new file mode 100644 index 00000000..e57b7f99 --- /dev/null +++ b/board-controller/generate_cert.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +""" +Generate self-signed SSL certificate for development +Usage: python generate_cert.py [--host IP_ADDRESS] +""" + +import argparse +import socket +from datetime import datetime, timedelta +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa + + +def generate_certificate(hostname="localhost", ip_address=None): + """Generate self-signed certificate""" + + # Generate private key + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + ) + + # Various subject attributes + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, "US"), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "CA"), + x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "BoardSesh Controller"), + x509.NameAttribute(NameOID.COMMON_NAME, hostname), + ]) + + # Build certificate + cert_builder = x509.CertificateBuilder() + cert_builder = cert_builder.subject_name(subject) + cert_builder = cert_builder.issuer_name(issuer) + cert_builder = cert_builder.public_key(private_key.public_key()) + cert_builder = cert_builder.serial_number(x509.random_serial_number()) + cert_builder = cert_builder.not_valid_before(datetime.utcnow()) + cert_builder = cert_builder.not_valid_after(datetime.utcnow() + timedelta(days=365)) + + # Add Subject Alternative Names + san_list = [x509.DNSName(hostname)] + if hostname != "localhost": + san_list.append(x509.DNSName("localhost")) + + if ip_address: + try: + san_list.append(x509.IPAddress(ip_address)) + except ValueError: + print(f"Warning: Invalid IP address format: {ip_address}") + + cert_builder = cert_builder.add_extension( + x509.SubjectAlternativeName(san_list), + critical=False, + ) + + # Sign certificate + certificate = cert_builder.sign(private_key, hashes.SHA256()) + + return private_key, certificate + + +def main(): + parser = argparse.ArgumentParser(description="Generate self-signed SSL certificate") + parser.add_argument("--host", default="localhost", help="Hostname for certificate") + parser.add_argument("--ip", help="IP address to include in certificate") + + args = parser.parse_args() + + # Auto-detect local IP if not provided + if not args.ip: + try: + # Connect to a remote address to determine local IP + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + args.ip = s.getsockname()[0] + s.close() + print(f"Auto-detected IP address: {args.ip}") + except Exception: + print("Could not auto-detect IP address") + + print(f"Generating certificate for host: {args.host}") + if args.ip: + print(f"Including IP address: {args.ip}") + + try: + import ipaddress + ip_obj = ipaddress.ip_address(args.ip) if args.ip else None + except ValueError: + print(f"Warning: Invalid IP address: {args.ip}") + ip_obj = None + + private_key, certificate = generate_certificate(args.host, ip_obj) + + # Write private key + with open("server.key", "wb") as f: + f.write(private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + )) + + # Write certificate + with open("server.crt", "wb") as f: + f.write(certificate.public_bytes(serialization.Encoding.PEM)) + + print("Certificate files generated:") + print("- server.key (private key)") + print("- server.crt (certificate)") + print() + print("To start the server with HTTPS/WSS:") + print("python main.py --ssl-cert server.crt --ssl-key server.key") + print() + print("NOTE: This is a self-signed certificate for development only.") + print("Browsers will show a security warning that you need to accept.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/board-controller/main.py b/board-controller/main.py index 8de70208..0ef05213 100644 --- a/board-controller/main.py +++ b/board-controller/main.py @@ -9,6 +9,7 @@ import json import logging import os +import ssl import sys import threading import uuid @@ -574,7 +575,9 @@ async def root(request: Request): # Get the request host to build controller URL host = request.url.hostname or "localhost" port = request.url.port or 8000 - controller_url = f"http://{host}:{port}" + # Use https if the request came over HTTPS (SSL enabled) + scheme = "https" if request.url.scheme == "https" else "http" + controller_url = f"{scheme}://{host}:{port}" encoded_controller_url = urllib.parse.quote(controller_url, safe='') # Default board configuration (can be made configurable later) @@ -669,32 +672,167 @@ async def _handle_websocket_message(self, data: dict, websocket: WebSocket): "source": self.controller_id }, self.session_id, exclude=websocket) - async def run(self, host: str = "0.0.0.0", port: int = 8000): - """Start the server""" - config = uvicorn.Config( - app=self.app, - host=host, - port=port, - log_level="info" - ) + async def run(self, host: str = "0.0.0.0", port: int = 8000, ssl_keyfile: str = None, ssl_certfile: str = None): + """Start the server with optional SSL support""" + config_kwargs = { + "app": self.app, + "host": host, + "port": port, + "log_level": "info" + } + + # Add SSL configuration if certificates are provided + if ssl_keyfile and ssl_certfile: + config_kwargs["ssl_keyfile"] = ssl_keyfile + config_kwargs["ssl_certfile"] = ssl_certfile + logger.info(f"Starting HTTPS/WSS server on {host}:{port}") + else: + logger.info(f"Starting HTTP/WS server on {host}:{port}") + logger.info("To enable HTTPS/WSS, provide --ssl-cert and --ssl-key parameters") + + config = uvicorn.Config(**config_kwargs) server = uvicorn.Server(config) await server.serve() +def auto_generate_ssl_cert(): + """Auto-generate self-signed SSL certificate if none exists""" + cert_path = Path("server.crt") + key_path = Path("server.key") + + if cert_path.exists() and key_path.exists(): + logger.info("Using existing SSL certificates") + return str(cert_path), str(key_path) + + logger.info("Generating self-signed SSL certificate...") + + try: + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import rsa + from datetime import datetime, timedelta + import socket + import ipaddress + + # Generate private key + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + ) + + # Get local IP + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + local_ip = s.getsockname()[0] + s.close() + except Exception: + local_ip = "127.0.0.1" + + # Certificate subject + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, "US"), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "CA"), + x509.NameAttribute(NameOID.LOCALITY_NAME, "Local"), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "BoardSesh Controller"), + x509.NameAttribute(NameOID.COMMON_NAME, "localhost"), + ]) + + # Build certificate + cert_builder = x509.CertificateBuilder() + cert_builder = cert_builder.subject_name(subject) + cert_builder = cert_builder.issuer_name(issuer) + cert_builder = cert_builder.public_key(private_key.public_key()) + cert_builder = cert_builder.serial_number(x509.random_serial_number()) + cert_builder = cert_builder.not_valid_before(datetime.utcnow()) + cert_builder = cert_builder.not_valid_after(datetime.utcnow() + timedelta(days=365)) + + # Add Subject Alternative Names (SAN) + san_list = [ + x509.DNSName("localhost"), + x509.IPAddress(ipaddress.ip_address("127.0.0.1")) + ] + + if local_ip != "127.0.0.1": + try: + san_list.append(x509.IPAddress(ipaddress.ip_address(local_ip))) + except Exception: + pass + + cert_builder = cert_builder.add_extension( + x509.SubjectAlternativeName(san_list), + critical=False, + ) + + # Sign certificate + certificate = cert_builder.sign(private_key, hashes.SHA256()) + + # Write files + with open(key_path, "wb") as f: + f.write(private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + )) + + with open(cert_path, "wb") as f: + f.write(certificate.public_bytes(serialization.Encoding.PEM)) + + logger.info(f"Generated SSL certificate for localhost and {local_ip}") + return str(cert_path), str(key_path) + + except ImportError: + logger.error("cryptography library required for SSL certificate generation") + logger.error("Run: pip install cryptography") + return None, None + except Exception as e: + logger.error(f"Failed to generate SSL certificate: {e}") + return None, None + + def main(): """Main entry point""" parser = argparse.ArgumentParser(description="Board Controller Server") parser.add_argument("--no-bluetooth", action="store_true", help="Run without Bluetooth support") parser.add_argument("--port", type=int, default=8000, help="Server port (default: 8000)") parser.add_argument("--host", type=str, default="0.0.0.0", help="Server host (default: 0.0.0.0)") + parser.add_argument("--ssl-cert", type=str, help="Path to SSL certificate file") + parser.add_argument("--ssl-key", type=str, help="Path to SSL private key file") + parser.add_argument("--auto-ssl", action="store_true", help="Auto-generate self-signed SSL certificate") args = parser.parse_args() + # Auto-generate SSL if requested or if running in Docker + ssl_cert, ssl_key = args.ssl_cert, args.ssl_key + if args.auto_ssl or (not ssl_cert and not ssl_key and os.getenv("DOCKER_CONTAINER")): + logger.info("Auto-generating SSL certificate...") + ssl_cert, ssl_key = auto_generate_ssl_cert() + + # Validate SSL arguments + if (ssl_cert and not ssl_key) or (ssl_key and not ssl_cert): + logger.error("Both SSL certificate and key must be provided together") + sys.exit(1) + + if ssl_cert and ssl_key: + # Verify SSL files exist + if not os.path.isfile(ssl_cert): + logger.error(f"SSL certificate file not found: {ssl_cert}") + sys.exit(1) + if not os.path.isfile(ssl_key): + logger.error(f"SSL key file not found: {ssl_key}") + sys.exit(1) + # Create and run controller controller = BoardController(no_bluetooth=args.no_bluetooth) try: - asyncio.run(controller.run(host=args.host, port=args.port)) + asyncio.run(controller.run( + host=args.host, + port=args.port, + ssl_keyfile=ssl_key, + ssl_certfile=ssl_cert + )) except KeyboardInterrupt: logger.info("Shutting down...") except Exception as e: diff --git a/board-controller/requirements.txt b/board-controller/requirements.txt index 9e1de242..739546d6 100644 --- a/board-controller/requirements.txt +++ b/board-controller/requirements.txt @@ -30,4 +30,7 @@ python-dotenv==1.0.1 # PyGObject # Utilities -uuid==1.30 \ No newline at end of file +uuid==1.30 + +# SSL certificate generation (for development) +cryptography==43.0.3 \ No newline at end of file From 0364855ac75b0192ae2537207b7deb044986d567 Mon Sep 17 00:00:00 2001 From: Marco de Jongh Date: Sun, 10 Aug 2025 18:33:11 +1000 Subject: [PATCH 3/3] Only publish docker on main --- .github/workflows/board-controller-docker.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/board-controller-docker.yml b/.github/workflows/board-controller-docker.yml index 4d015745..28bddbeb 100644 --- a/.github/workflows/board-controller-docker.yml +++ b/.github/workflows/board-controller-docker.yml @@ -6,11 +6,6 @@ on: paths: - 'board-controller/**' - '.github/workflows/board-controller-docker.yml' - pull_request: - branches: [ main ] - paths: - - 'board-controller/**' - - '.github/workflows/board-controller-docker.yml' release: types: [published] @@ -53,11 +48,12 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image + id: build uses: docker/build-push-action@v5 with: context: ./board-controller platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha @@ -65,7 +61,6 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 - if: github.event_name != 'pull_request' with: subject-name: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.build.outputs.digest }}