init
This commit is contained in:
7
.claude/settings.local.json
Normal file
7
.claude/settings.local.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(chmod +x /Users/vasyansk/Developers/MyProject/IaaC/Realmanual/elevenlabs.io/proxy-vm/scripts/install.sh /Users/vasyansk/Developers/MyProject/IaaC/Realmanual/elevenlabs.io/proxy-vm/scripts/deploy.sh /Users/vasyansk/Developers/MyProject/IaaC/Realmanual/elevenlabs.io/proxy-vm/docker-entrypoint.sh)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
38
.gitea/build.yaml
Normal file
38
.gitea/build.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Build Admin
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.realmanual.ru
|
||||||
|
IMAGE_PREFIX: ${{ gitea.repository }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build image
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: catthehacker/ubuntu:act-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Read Version
|
||||||
|
id: version
|
||||||
|
run: echo "VERSION=$(cat admin/VERSION)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Log in to Gitea Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ./admin/
|
||||||
|
file: ./admin/Dockerfile.admin
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/elevenlabs:${{ steps.version.outputs.VERSION }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/elevenlabs:latest
|
||||||
7
proxy-vm/.env.example
Normal file
7
proxy-vm/.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Secret token — k8s pods must send this in X-Proxy-Token header
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
PROXY_SECRET=change-me-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# Allowed source CIDRs (comma-separated), leave empty to allow all (dev mode)
|
||||||
|
# Example: "10.0.0.0/8,172.16.0.0/12"
|
||||||
|
ALLOWED_CIDR=
|
||||||
22
proxy-vm/Dockerfile
Normal file
22
proxy-vm/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# proxy-vm/Dockerfile
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
# Remove default config
|
||||||
|
RUN rm -f /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Copy nginx config
|
||||||
|
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Copy entrypoint script
|
||||||
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
|
||||||
|
# Create conf.d directory for generated configs
|
||||||
|
RUN mkdir -p /etc/nginx/conf.d
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:8080/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
212
proxy-vm/README.md
Normal file
212
proxy-vm/README.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# API Proxy — HTTPS Reverse Proxy
|
||||||
|
|
||||||
|
Production-ready nginx reverse proxy in Docker for forwarding requests from an internal Kubernetes cluster to external APIs (ElevenLabs, OpenAI, etc.)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Ubuntu 24.04 VM
|
||||||
|
- Port 8080 open **only** to k8s cluster CIDR (firewall rule)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On a fresh VM (as root):
|
||||||
|
sudo bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit environment variables:
|
||||||
|
vi /opt/proxy/proxy-vm/.env
|
||||||
|
|
||||||
|
# Set your k8s cluster CIDR:
|
||||||
|
ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12
|
||||||
|
|
||||||
|
# Restart to apply:
|
||||||
|
cd /opt/proxy/proxy-vm
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check (no auth required):
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
|
||||||
|
# Test with auth:
|
||||||
|
curl -H "X-Proxy-Token: YOUR_SECRET" http://localhost:8080/elevenlabs/v1/voices
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Add a New Upstream
|
||||||
|
|
||||||
|
Example: adding `api.anthropic.com` → `/anthropic/*`
|
||||||
|
|
||||||
|
### 1. Add upstream block in `nginx/nginx.conf`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream anthropic_backend {
|
||||||
|
server api.anthropic.com:443;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add location block in the `server` section:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /anthropic/ {
|
||||||
|
if ($allowed_ip = 0) {
|
||||||
|
return 403 '{"error":"ip_not_allowed"}';
|
||||||
|
}
|
||||||
|
if ($auth_ok = 0) {
|
||||||
|
return 403 '{"error":"invalid_token"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite ^/anthropic/(.*) /$1 break;
|
||||||
|
|
||||||
|
proxy_pass https://anthropic_backend;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
proxy_ssl_name api.anthropic.com;
|
||||||
|
|
||||||
|
proxy_set_header Host api.anthropic.com;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header X-Proxy-Token "";
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Rebuild and restart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/proxy/proxy-vm
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## K8s Side: How to Call the Proxy
|
||||||
|
|
||||||
|
### Environment Variables (in your Deployment/ConfigMap)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: PROXY_BASE_URL
|
||||||
|
value: "http://10.0.1.50:8080" # proxy VM internal IP
|
||||||
|
- name: PROXY_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: api-proxy
|
||||||
|
key: token
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// In tts-worker, replace ELEVENLABS_BASE_URL:
|
||||||
|
// Before: https://api.elevenlabs.io
|
||||||
|
// After: http://<proxy-vm-ip>:8080/elevenlabs
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`${process.env.PROXY_BASE_URL}/elevenlabs/v1/text-to-speech/${voiceId}`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'xi-api-key': process.env.ELEVENLABS_API_KEY, // passed through to upstream
|
||||||
|
'X-Proxy-Token': process.env.PROXY_SECRET, // validated by proxy
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Response is binary MP3, identical to direct ElevenLabs call
|
||||||
|
const buffer = Buffer.from(await response.arrayBuffer());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
- [ ] `PROXY_SECRET` generated via `openssl rand -hex 32`
|
||||||
|
- [ ] `ALLOWED_CIDR` restricted to cluster CIDR only
|
||||||
|
- [ ] Port 8080 closed to the public (only k8s CIDR in firewall/NSG)
|
||||||
|
- [ ] VM has no public IP or is behind NAT
|
||||||
|
- [ ] Logs rotate (Docker logging driver configured: 50m x 5 files)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding TLS (Self-Signed)
|
||||||
|
|
||||||
|
If you need HTTPS between k8s and the proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate self-signed cert:
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||||
|
-keyout /opt/proxy/proxy-vm/nginx/ssl/proxy.key \
|
||||||
|
-out /opt/proxy/proxy-vm/nginx/ssl/proxy.crt \
|
||||||
|
-subj "/CN=api-proxy"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in `nginx.conf`, change the server block:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 8443 ssl;
|
||||||
|
ssl_certificate /etc/nginx/ssl/proxy.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/proxy.key;
|
||||||
|
# ... rest of config
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Mount the certs in `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Tail logs in JSON format
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f proxy | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Count requests per upstream per minute
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs --since=1m proxy --no-log-prefix \
|
||||||
|
| jq -r '.uri' \
|
||||||
|
| cut -d'/' -f2 \
|
||||||
|
| sort | uniq -c | sort -rn
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check container health
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker inspect --format='{{.State.Health.Status}}' api-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. A k8s pod sends an HTTP request to `http://<proxy-vm>:8080/elevenlabs/v1/text-to-speech/...` with the `X-Proxy-Token` header and the original API key (`xi-api-key`).
|
||||||
|
2. Nginx checks the source IP against the configured `ALLOWED_CIDR` geo block and validates the `X-Proxy-Token` against `PROXY_SECRET` — rejecting with 403 if either fails.
|
||||||
|
3. The `/elevenlabs/` prefix is stripped via rewrite, and the request is forwarded over HTTPS to `api.elevenlabs.io` with SNI enabled and keepalive connections.
|
||||||
|
4. The `X-Proxy-Token` header is removed before forwarding; all other headers (including `xi-api-key`) pass through unchanged.
|
||||||
|
5. The response streams back unbuffered to the pod — critical for binary audio data from ElevenLabs TTS.
|
||||||
21
proxy-vm/docker-compose.yml
Normal file
21
proxy-vm/docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# proxy-vm/docker-compose.yml
|
||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
proxy:
|
||||||
|
build: .
|
||||||
|
container_name: api-proxy
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
env_file: .env
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "5"
|
||||||
56
proxy-vm/docker-entrypoint.sh
Executable file
56
proxy-vm/docker-entrypoint.sh
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# proxy-vm/docker-entrypoint.sh
|
||||||
|
# Generates nginx config fragments from environment variables at container start.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONF_DIR="/etc/nginx/conf.d"
|
||||||
|
mkdir -p "${CONF_DIR}"
|
||||||
|
|
||||||
|
# --- 1. Generate IP allowlist (geo block) ---
|
||||||
|
ALLOWLIST_FILE="${CONF_DIR}/allowlist.conf"
|
||||||
|
|
||||||
|
if [ -z "${ALLOWED_CIDR}" ]; then
|
||||||
|
# Dev mode: allow all IPs
|
||||||
|
cat > "${ALLOWLIST_FILE}" <<'GEO'
|
||||||
|
geo $allowed_ip {
|
||||||
|
default 1;
|
||||||
|
}
|
||||||
|
GEO
|
||||||
|
echo "[entrypoint] ALLOWED_CIDR is empty — allowing all IPs (dev mode)"
|
||||||
|
else
|
||||||
|
# Build geo block from comma-separated CIDRs
|
||||||
|
{
|
||||||
|
echo 'geo $allowed_ip {'
|
||||||
|
echo ' default 0;'
|
||||||
|
echo "${ALLOWED_CIDR}" | tr ',' '\n' | while read -r cidr; do
|
||||||
|
cidr=$(echo "${cidr}" | xargs) # trim whitespace
|
||||||
|
[ -n "${cidr}" ] && echo " ${cidr} 1;"
|
||||||
|
done
|
||||||
|
echo '}'
|
||||||
|
} > "${ALLOWLIST_FILE}"
|
||||||
|
echo "[entrypoint] IP allowlist configured: ${ALLOWED_CIDR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 2. Generate token auth (map block) ---
|
||||||
|
AUTH_FILE="${CONF_DIR}/auth.conf"
|
||||||
|
|
||||||
|
if [ -z "${PROXY_SECRET}" ]; then
|
||||||
|
echo "[entrypoint] WARNING: PROXY_SECRET is not set — all requests will be rejected!"
|
||||||
|
cat > "${AUTH_FILE}" <<'MAP'
|
||||||
|
map $http_x_proxy_token $auth_ok {
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
MAP
|
||||||
|
else
|
||||||
|
cat > "${AUTH_FILE}" <<MAP
|
||||||
|
map \$http_x_proxy_token \$auth_ok {
|
||||||
|
default 0;
|
||||||
|
"${PROXY_SECRET}" 1;
|
||||||
|
}
|
||||||
|
MAP
|
||||||
|
echo "[entrypoint] Token auth configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 3. Start nginx ---
|
||||||
|
echo "[entrypoint] Starting nginx..."
|
||||||
|
exec nginx -g 'daemon off;'
|
||||||
136
proxy-vm/nginx/nginx.conf
Normal file
136
proxy-vm/nginx/nginx.conf
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# proxy-vm/nginx/nginx.conf
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
# --- JSON access log ---
|
||||||
|
log_format json_log escape=json
|
||||||
|
'{'
|
||||||
|
'"time":"$time_iso8601",'
|
||||||
|
'"remote_addr":"$remote_addr",'
|
||||||
|
'"method":"$request_method",'
|
||||||
|
'"uri":"$request_uri",'
|
||||||
|
'"status":$status,'
|
||||||
|
'"bytes_sent":$bytes_sent,'
|
||||||
|
'"request_time":$request_time,'
|
||||||
|
'"upstream_addr":"$upstream_addr",'
|
||||||
|
'"upstream_response_time":"$upstream_response_time"'
|
||||||
|
'}';
|
||||||
|
|
||||||
|
access_log /dev/stdout json_log;
|
||||||
|
|
||||||
|
# --- Performance ---
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
client_max_body_size 50m;
|
||||||
|
gzip off;
|
||||||
|
|
||||||
|
# --- IP allowlist (generated at container start) ---
|
||||||
|
include /etc/nginx/conf.d/allowlist.conf;
|
||||||
|
|
||||||
|
# --- Token auth ---
|
||||||
|
include /etc/nginx/conf.d/auth.conf;
|
||||||
|
|
||||||
|
# --- Upstreams with keepalive ---
|
||||||
|
upstream elevenlabs_backend {
|
||||||
|
server api.elevenlabs.io:443;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream openai_backend {
|
||||||
|
server api.openai.com:443;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# --- Health check (no auth) ---
|
||||||
|
location = /health {
|
||||||
|
access_log off;
|
||||||
|
default_type application/json;
|
||||||
|
return 200 '{"status":"ok","version":"1.0"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- ElevenLabs ---
|
||||||
|
location /elevenlabs/ {
|
||||||
|
# Auth checks
|
||||||
|
if ($allowed_ip = 0) {
|
||||||
|
return 403 '{"error":"ip_not_allowed"}';
|
||||||
|
}
|
||||||
|
if ($auth_ok = 0) {
|
||||||
|
return 403 '{"error":"invalid_token"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
# Strip /elevenlabs/ prefix and proxy
|
||||||
|
rewrite ^/elevenlabs/(.*) /$1 break;
|
||||||
|
|
||||||
|
proxy_pass https://elevenlabs_backend;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
proxy_ssl_name api.elevenlabs.io;
|
||||||
|
|
||||||
|
# Pass original Host header for SNI
|
||||||
|
proxy_set_header Host api.elevenlabs.io;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
|
# Do NOT forward proxy token to upstream
|
||||||
|
proxy_set_header X-Proxy-Token "";
|
||||||
|
|
||||||
|
# HTTP/1.1 for keepalive
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Streaming / performance
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- OpenAI ---
|
||||||
|
location /openai/ {
|
||||||
|
if ($allowed_ip = 0) {
|
||||||
|
return 403 '{"error":"ip_not_allowed"}';
|
||||||
|
}
|
||||||
|
if ($auth_ok = 0) {
|
||||||
|
return 403 '{"error":"invalid_token"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite ^/openai/(.*) /$1 break;
|
||||||
|
|
||||||
|
proxy_pass https://openai_backend;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
proxy_ssl_name api.openai.com;
|
||||||
|
|
||||||
|
proxy_set_header Host api.openai.com;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header X-Proxy-Token "";
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Catch-all ---
|
||||||
|
location / {
|
||||||
|
default_type application/json;
|
||||||
|
return 404 '{"error":"unknown_upstream","hint":"use /elevenlabs/ or /openai/"}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
proxy-vm/scripts/deploy.sh
Executable file
25
proxy-vm/scripts/deploy.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# proxy-vm/scripts/deploy.sh
|
||||||
|
# Pull latest changes and redeploy containers.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROXY_DIR="/opt/proxy/proxy-vm"
|
||||||
|
cd "${PROXY_DIR}"
|
||||||
|
|
||||||
|
echo "=== [1/5] Pulling latest code ==="
|
||||||
|
git -C "$(dirname "${PROXY_DIR}")" pull
|
||||||
|
|
||||||
|
echo "=== [2/5] Building container ==="
|
||||||
|
docker compose build --no-cache
|
||||||
|
|
||||||
|
echo "=== [3/5] Restarting containers ==="
|
||||||
|
docker compose up -d --force-recreate
|
||||||
|
|
||||||
|
echo "=== [4/5] Container status ==="
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
echo "=== [5/5] Recent logs ==="
|
||||||
|
docker compose logs --tail=20 proxy
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Deploy complete."
|
||||||
53
proxy-vm/scripts/install.sh
Executable file
53
proxy-vm/scripts/install.sh
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# proxy-vm/scripts/install.sh
|
||||||
|
# Bootstrap script for fresh Ubuntu 24.04 VM.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROXY_DIR="/opt/proxy"
|
||||||
|
REPO_URL="https://github.com/YOUR_ORG/YOUR_REPO.git" # TODO: replace with actual repo
|
||||||
|
|
||||||
|
echo "=== [1/6] Installing Docker and dependencies ==="
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y docker.io docker-compose-plugin git curl
|
||||||
|
|
||||||
|
echo "=== [2/6] Enabling Docker ==="
|
||||||
|
systemctl enable --now docker
|
||||||
|
|
||||||
|
echo "=== [3/6] Creating project directory ==="
|
||||||
|
mkdir -p "${PROXY_DIR}"
|
||||||
|
|
||||||
|
echo "=== [4/6] Cloning repository ==="
|
||||||
|
if [ -d "${PROXY_DIR}/.git" ]; then
|
||||||
|
echo "Repository already exists, pulling latest..."
|
||||||
|
git -C "${PROXY_DIR}" pull
|
||||||
|
else
|
||||||
|
git clone "${REPO_URL}" "${PROXY_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== [5/6] Setting up .env ==="
|
||||||
|
cd "${PROXY_DIR}/proxy-vm"
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
cp .env.example .env
|
||||||
|
# Generate a random PROXY_SECRET
|
||||||
|
GENERATED_SECRET=$(openssl rand -hex 32)
|
||||||
|
sed -i "s/change-me-generate-with-openssl-rand-hex-32/${GENERATED_SECRET}/" .env
|
||||||
|
echo ""
|
||||||
|
echo ">>> .env created with auto-generated PROXY_SECRET"
|
||||||
|
echo ">>> Edit /opt/proxy/proxy-vm/.env to set ALLOWED_CIDR"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo ".env already exists, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== [6/6] Starting containers ==="
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Proxy is running on port 8080"
|
||||||
|
echo " Health check: curl http://localhost:8080/health"
|
||||||
|
echo ""
|
||||||
|
echo " IMPORTANT: Edit ${PROXY_DIR}/proxy-vm/.env"
|
||||||
|
echo " - Set ALLOWED_CIDR to your k8s cluster CIDR"
|
||||||
|
echo " - Then run: docker compose restart"
|
||||||
|
echo "=========================================="
|
||||||
Reference in New Issue
Block a user