Skip to content

Commit 7cd3d77

Browse files
author
Sagar
committed
deployment config
1 parent 5bb1ab6 commit 7cd3d77

9 files changed

Lines changed: 389 additions & 8 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# ─────────────────────────────────────────────
2+
# GitHub Actions — Deploy Frontend to Azure Static Web Apps
3+
#
4+
# Setup (one-time):
5+
# 1. Create your Azure Static Web App in the portal
6+
# 2. Go to your Static Web App → Manage deployment token
7+
# 3. Add it as a GitHub repo secret:
8+
# Settings → Secrets → Actions → New secret
9+
# Name: AZURE_STATIC_WEB_APPS_API_TOKEN
10+
# 4. Add your VM's public IP or domain as another secret:
11+
# Name: VITE_API_BASE_URL value: http://YOUR_VM_IP
12+
#
13+
# Triggers: every push to main
14+
# ─────────────────────────────────────────────
15+
16+
name: Deploy Frontend to Azure Static Web Apps
17+
18+
on:
19+
push:
20+
branches:
21+
- main
22+
paths:
23+
- "frontend/**"
24+
- ".github/workflows/deploy-frontend.yml"
25+
workflow_dispatch: # allow manual trigger from GitHub UI
26+
27+
jobs:
28+
build_and_deploy:
29+
runs-on: ubuntu-latest
30+
name: Build and Deploy
31+
32+
steps:
33+
- name: Checkout repository
34+
uses: actions/checkout@v4
35+
36+
- name: Set up Node.js
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version: "20"
40+
cache: "npm"
41+
cache-dependency-path: frontend/package-lock.json
42+
43+
- name: Install dependencies
44+
working-directory: frontend
45+
run: npm ci
46+
47+
- name: Build React app
48+
working-directory: frontend
49+
env:
50+
# Injected from GitHub secret — your VM's public IP or domain
51+
VITE_API_BASE_URL: ${{ secrets.VITE_API_BASE_URL }}
52+
run: npm run build
53+
54+
- name: Deploy to Azure Static Web Apps
55+
uses: Azure/static-web-apps-deploy@v1
56+
with:
57+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
58+
repo_token: ${{ secrets.GITHUB_TOKEN }}
59+
action: "upload"
60+
app_location: "frontend"
61+
output_location: "dist"
62+
skip_app_build: true # we built above with env vars injected

api/.env.example

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
# Legal Bolt Environment Configuration
1+
# ─────────────────────────────────────────────
2+
# JurisFind API – Environment Variables Template
3+
# Copy this to .env and fill in real values
4+
# NEVER commit the real .env to git
5+
# ─────────────────────────────────────────────
26

3-
# Groq API Configuration
4-
# Get your API key from: https://console.groq.com/keys
5-
GROQ_API_KEY="your API Key here"
6-
# Optional: Model Configuration
7+
# ── Groq API ─────────────────────────────────
8+
# Get from: https://console.groq.com/keys
9+
GROQ_API_KEY=your_groq_api_key_here
710
GROQ_MODEL=llama-3.3-70b-versatile
811

9-
# Optional: API Configuration
12+
# ── Azure Blob Storage ────────────────────────
13+
# Get from: Azure Portal → Storage Account → Access Keys → Connection string
14+
# Uncomment when deploying to production with Azure Blob
15+
# AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=YOUR_ACCOUNT_NAME;AccountKey=YOUR_ACCOUNT_KEY;EndpointSuffix=core.windows.net
16+
# AZURE_DATA_CONTAINER=data
17+
18+
# ── Storage Mode ─────────────────────────────
19+
# true = use local api/data/ folder (local dev)
20+
# false = use Azure Blob Storage (production)
21+
USE_LOCAL_FILES=true
22+
23+
# ── Server ───────────────────────────────────
24+
# Use 0.0.0.0 on VM (Docker), localhost for local dev
1025
API_HOST=localhost
1126
API_PORT=8000

api/Dockerfile

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# ─────────────────────────────────────────────
2+
# JurisFind API – Dockerfile
3+
# Base: Python 3.11 slim (compatible with all requirements)
4+
# ─────────────────────────────────────────────
5+
6+
FROM python:3.11-slim
7+
8+
# Prevent Python from writing .pyc files and enable unbuffered stdout
9+
ENV PYTHONDONTWRITEBYTECODE=1 \
10+
PYTHONUNBUFFERED=1 \
11+
PIP_NO_CACHE_DIR=1 \
12+
PIP_DISABLE_PIP_VERSION_CHECK=1
13+
14+
# System dependencies needed by PyMuPDF, torch, faiss
15+
RUN apt-get update && apt-get install -y --no-install-recommends \
16+
build-essential \
17+
libgl1 \
18+
libglib2.0-0 \
19+
libgomp1 \
20+
curl \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
WORKDIR /app
24+
25+
# Install Python dependencies first (layer cache)
26+
COPY requirements.txt .
27+
RUN pip install --upgrade pip && \
28+
pip install -r requirements.txt
29+
30+
# Copy application source
31+
COPY . .
32+
33+
# Create directory for temporary confidential uploads
34+
RUN mkdir -p /tmp/confidential_uploads
35+
36+
# Expose the uvicorn port
37+
EXPOSE 8000
38+
39+
# Health check – hits the /api/health endpoint
40+
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
41+
CMD curl -f http://localhost:8000/api/health || exit 1
42+
43+
# Run with uvicorn factory mode — matches how main.py calls uvicorn.run()
44+
# 2 workers is fine for Standard_B2s (2 vCPU). Increase to 4 on larger VMs.
45+
CMD ["uvicorn", "main:create_app", \
46+
"--factory", \
47+
"--host", "0.0.0.0", \
48+
"--port", "8000", \
49+
"--workers", "2", \
50+
"--timeout-keep-alive", "5"]

api/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def create_app():
3838
allow_origins=[
3939
"http://localhost:5173", # Vite dev server
4040
"http://localhost:3000", # React dev server
41-
# Add your Azure Static Web App URL here when deploying:
41+
"http://20.186.113.106", # Azure VM public IP
42+
# Add your Azure Static Web App URL here after deploying:
4243
# "https://your-app.azurestaticapps.net",
4344
],
4445
allow_credentials=True,

deploy.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/bin/bash
2+
# ─────────────────────────────────────────────
3+
# JurisFind – Azure VM Bootstrap Script
4+
#
5+
# Run this once after SSH-ing into a fresh Ubuntu 22.04 VM:
6+
# chmod +x deploy.sh
7+
# sudo ./deploy.sh
8+
#
9+
# What it does:
10+
# 1. Installs Docker + Docker Compose
11+
# 2. Installs Nginx
12+
# 3. Clones/updates the repo
13+
# 4. Copies Nginx config and reloads
14+
# 5. Starts the API container via Docker Compose
15+
# ─────────────────────────────────────────────
16+
17+
set -euo pipefail
18+
19+
REPO_URL="https://github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME.git"
20+
APP_DIR="/opt/jurisfind"
21+
NGINX_CONF="/etc/nginx/sites-available/jurisfind"
22+
23+
echo "════════════════════════════════════════"
24+
echo " JurisFind VM Setup"
25+
echo "════════════════════════════════════════"
26+
27+
# ── 1. System update ─────────────────────────
28+
echo "[1/6] Updating system packages..."
29+
apt-get update -q && apt-get upgrade -y -q
30+
31+
# ── 2. Install Docker ────────────────────────
32+
echo "[2/6] Installing Docker..."
33+
if ! command -v docker &> /dev/null; then
34+
curl -fsSL https://get.docker.com | sh
35+
systemctl enable docker
36+
systemctl start docker
37+
# Allow current user to run docker without sudo
38+
usermod -aG docker "$SUDO_USER" || true
39+
echo "Docker installed. You may need to log out and back in for group changes."
40+
else
41+
echo "Docker already installed: $(docker --version)"
42+
fi
43+
44+
# Docker Compose (v2 plugin)
45+
if ! docker compose version &> /dev/null; then
46+
apt-get install -y docker-compose-plugin
47+
fi
48+
echo "Docker Compose: $(docker compose version)"
49+
50+
# ── 3. Install Nginx ─────────────────────────
51+
echo "[3/6] Installing Nginx..."
52+
apt-get install -y nginx
53+
systemctl enable nginx
54+
systemctl start nginx
55+
56+
# ── 4. Clone / update repo ───────────────────
57+
echo "[4/6] Setting up application directory..."
58+
if [ -d "$APP_DIR/.git" ]; then
59+
echo "Repo exists — pulling latest..."
60+
git -C "$APP_DIR" pull
61+
else
62+
echo "Cloning repo to $APP_DIR..."
63+
git clone "$REPO_URL" "$APP_DIR"
64+
fi
65+
66+
# ── 5. Configure Nginx ───────────────────────
67+
echo "[5/6] Configuring Nginx..."
68+
cp "$APP_DIR/nginx.conf" "$NGINX_CONF"
69+
ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/jurisfind
70+
rm -f /etc/nginx/sites-enabled/default
71+
nginx -t && systemctl reload nginx
72+
echo "Nginx configured and reloaded."
73+
74+
# ── 6. Start API container ───────────────────
75+
echo "[6/6] Starting JurisFind API container..."
76+
77+
# Make sure .env exists
78+
if [ ! -f "$APP_DIR/api/.env" ]; then
79+
echo ""
80+
echo "⚠️ WARNING: $APP_DIR/api/.env not found!"
81+
echo " Copy api/.env.example → api/.env and fill in:"
82+
echo " GROQ_API_KEY"
83+
echo " AZURE_STORAGE_CONNECTION_STRING (if using Azure Blob)"
84+
echo " Then run: cd $APP_DIR && docker compose up -d --build"
85+
echo ""
86+
else
87+
cd "$APP_DIR"
88+
docker compose pull 2>/dev/null || true
89+
docker compose up -d --build
90+
echo "Container started. Check logs with: docker compose logs -f api"
91+
fi
92+
93+
echo ""
94+
echo "════════════════════════════════════════"
95+
echo " Setup complete!"
96+
echo " API available at: http://$(curl -s ifconfig.me)/api/health"
97+
echo " API docs at: http://$(curl -s ifconfig.me)/docs"
98+
echo ""
99+
echo " Next steps:"
100+
echo " 1. Fill in $APP_DIR/api/.env with real keys"
101+
echo " 2. Open ports 80 and 443 in Azure Network Security Group"
102+
echo " 3. (Optional) Add SSL: sudo certbot --nginx -d yourdomain.com"
103+
echo "════════════════════════════════════════"

docker-compose.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ─────────────────────────────────────────────
2+
# JurisFind – Docker Compose (Production on Azure VM)
3+
# Usage:
4+
# docker compose up -d → start
5+
# docker compose down → stop
6+
# docker compose logs -f api → live logs
7+
# docker compose pull && docker compose up -d --build → redeploy
8+
# ─────────────────────────────────────────────
9+
10+
version: "3.9"
11+
12+
services:
13+
api:
14+
build:
15+
context: ./api
16+
dockerfile: Dockerfile
17+
container_name: jurisfind_api
18+
restart: always
19+
ports:
20+
# Only expose to localhost — Nginx proxies from port 80 to here
21+
- "127.0.0.1:8000:8000"
22+
env_file:
23+
# Copy api/.env.example → api/.env on the VM and fill in real values
24+
- ./api/.env
25+
volumes:
26+
# Persist local FAISS index & PDFs if not using Azure Blob
27+
- ./api/data:/app/data
28+
# Temp dir for confidential uploads (cleared between restarts is fine)
29+
- confidential_tmp:/tmp/confidential_uploads
30+
healthcheck:
31+
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
32+
interval: 30s
33+
timeout: 10s
34+
retries: 3
35+
start_period: 90s # give sentence-transformers time to load on first boot
36+
37+
volumes:
38+
confidential_tmp:

frontend/staticwebapp.config.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"routes": [
3+
{
4+
"route": "/api/*",
5+
"allowedRoles": ["anonymous"]
6+
},
7+
{
8+
"route": "/*",
9+
"serve": "/index.html",
10+
"statusCode": 200
11+
}
12+
],
13+
"navigationFallback": {
14+
"rewrite": "/index.html",
15+
"exclude": ["/assets/*", "/*.{js,css,png,ico,svg,woff,woff2,ttf}"]
16+
},
17+
"mimeTypes": {
18+
".json": "application/json"
19+
},
20+
"globalHeaders": {
21+
"X-Content-Type-Options": "nosniff",
22+
"X-Frame-Options": "DENY",
23+
"X-XSS-Protection": "1; mode=block"
24+
}
25+
}

frontend/vercel.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
}
1616
],
1717
"env": {
18-
"VITE_API_BASE_URL": "http://13.71.23.132"
18+
"VITE_API_BASE_URL": "http://20.186.113.106"
1919
}
2020
}

0 commit comments

Comments
 (0)