Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/build_dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build and Push Docker Images

on:
push:
branches:
- 'dev'
workflow_dispatch:

jobs:
api:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
context: ./backend
tags: erasme/ai-proxy:${{ github.ref == 'refs/heads/main' && 'latest' || 'dev' }}
59 changes: 59 additions & 0 deletions .github/workflows/build_main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Update Version and build main branch

on:
pull_request:
types:
- closed
branches:
- main # This ensures that only PRs merged into 'main' will trigger this workflow.

permissions:
contents: write

jobs:
create_tag:
runs-on: ubuntu-latest
steps:
### Step 1: Check out the repository
- name: Checkout Code
if: ${{ github.event.pull_request.merged == true }}
uses: actions/checkout@v3

### Step 2: Configure Git
- name: Configure Git
if: ${{ github.event.pull_request.merged == true }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

### Step 3: Bump the Version and Create Tag
- name: Bump Version and Create Tag
if: ${{ github.event.pull_request.merged == true }}
id: version
uses: anothrNick/github-tag-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INITIAL_VERSION: 0.1.0
DEFAULT_BUMP: patch

- name: Checkout
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
context: ./backend
tags: erasme/ai-proxy:latest , erasme/ai-proxy:${{ steps.version.outputs.new_tag }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ config.yaml
*.db
emissions.csv
backend/.~lock.emissions.csv#
data/
data/
venv
211 changes: 0 additions & 211 deletions backend/exporter.py

This file was deleted.

49 changes: 49 additions & 0 deletions backend/lib/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import secrets
import base64
import os
import yaml
from fastapi import Request, Response

# In your config.yaml loading section
with open("/config.yaml", "r") as f:
CONFIG = yaml.safe_load(f)

# Get metrics auth from config or environment
METRICS_AUTH = CONFIG.get('metrics_auth', {})
METRICS_USERNAME = METRICS_AUTH.get('username', 'admin')
METRICS_PASSWORD = METRICS_AUTH.get('password', 'change-me')

def verify_metrics_auth(credentials: str) -> bool:
"""Verify HTTP Basic Auth credentials for metrics endpoint"""
try:
decoded = base64.b64decode(credentials).decode("utf-8")
username, password = decoded.split(":", 1)
# Use secrets.compare_digest to prevent timing attacks
username_correct = secrets.compare_digest(username, METRICS_USERNAME)
password_correct = secrets.compare_digest(password, METRICS_PASSWORD)
return username_correct and password_correct
except Exception:
return False

async def metrics_auth_middleware(request: Request, call_next):
"""Middleware to protect /metrics endpoint with Basic Auth"""
if request.url.path.startswith("/metrics"):
auth_header = request.headers.get("Authorization")

if not auth_header or not auth_header.startswith("Basic "):
return Response(
content="Unauthorized",
status_code=401,
headers={"WWW-Authenticate": 'Basic realm="Metrics"'}
)

credentials = auth_header[6:] # Remove "Basic " prefix
if not verify_metrics_auth(credentials):
return Response(
content="Invalid credentials",
status_code=401,
headers={"WWW-Authenticate": 'Basic realm="Metrics"'}
)

response = await call_next(request)
return response
Loading