Skip to content

Commit a253093

Browse files
committed
initial commit
0 parents  commit a253093

19 files changed

+1916
-0
lines changed

.github/workflows/build.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Build
2+
3+
on:
4+
push:
5+
branches:
6+
- '*'
7+
- '!main'
8+
jobs:
9+
build:
10+
name: Build
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Check out code
14+
uses: actions/checkout@v4
15+
16+
- name: Build docker image
17+
run: |
18+
docker build .
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Docker Build, Push, and Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
env:
9+
REGISTRY: ghcr.io
10+
IMAGE_NAME: ${{ github.repository }}
11+
KEEP_RELEASES: 10
12+
KEEP_IMAGES: 10
13+
14+
jobs:
15+
build-push-release:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: write
19+
packages: write
20+
pull-requests: read
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0
27+
28+
- name: Get version from package.json
29+
id: package-version
30+
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
31+
32+
- name: Set up Docker Buildx
33+
uses: docker/setup-buildx-action@v3
34+
35+
- name: Cache Docker layers
36+
uses: actions/cache@v3
37+
with:
38+
path: /tmp/.buildx-cache
39+
key: ${{ runner.os }}-buildx-${{ github.sha }}
40+
restore-keys: |
41+
${{ runner.os }}-buildx-
42+
43+
- name: Cache npm dependencies
44+
uses: actions/cache@v3
45+
with:
46+
path: ~/.npm
47+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
48+
restore-keys: |
49+
${{ runner.os }}-node-
50+
51+
- name: Log in to the Container registry
52+
uses: docker/login-action@v3
53+
with:
54+
registry: ${{ env.REGISTRY }}
55+
username: ${{ github.actor }}
56+
password: ${{ secrets.GITHUB_TOKEN }}
57+
58+
- name: Extract metadata (tags, labels) for Docker
59+
id: meta
60+
uses: docker/metadata-action@v5
61+
with:
62+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
63+
tags: |
64+
type=raw,value=${{ steps.package-version.outputs.VERSION }}
65+
66+
- name: Build and push Docker image
67+
uses: docker/build-push-action@v5
68+
with:
69+
context: .
70+
push: true
71+
tags: ${{ steps.meta.outputs.tags }}
72+
labels: ${{ steps.meta.outputs.labels }}
73+
cache-from: type=local,src=/tmp/.buildx-cache
74+
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
75+
76+
- name: Move cache
77+
run: |
78+
rm -rf /tmp/.buildx-cache
79+
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
80+
81+
# - name: Scan Docker image for vulnerabilities
82+
# uses: aquasecurity/trivy-action@master
83+
# env:
84+
# TRIVY_USERNAME: ${{ github.actor }}
85+
# TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
86+
# with:
87+
# image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.package-version.outputs.VERSION }}
88+
# format: 'table'
89+
# exit-code: '1'
90+
# ignore-unfixed: true
91+
# vuln-type: 'os,library'
92+
# severity: 'CRITICAL,HIGH'
93+
94+
- name: Get Pull Request Messages
95+
id: pr-messages
96+
run: |
97+
PR_MESSAGES=$(git log --merges --format="%b" @~1..HEAD | sed 's/^/* /')
98+
echo "PR_MESSAGES<<EOF" >> $GITHUB_OUTPUT
99+
echo "$PR_MESSAGES" >> $GITHUB_OUTPUT
100+
echo "EOF" >> $GITHUB_OUTPUT
101+
102+
- name: Get Commit Messages
103+
id: commit-messages
104+
run: |
105+
COMMIT_MESSAGES=$(git log --no-merges --format="* %s" @~1..HEAD)
106+
echo "COMMIT_MESSAGES<<EOF" >> $GITHUB_OUTPUT
107+
echo "$COMMIT_MESSAGES" >> $GITHUB_OUTPUT
108+
echo "EOF" >> $GITHUB_OUTPUT
109+
110+
- name: Create Release
111+
id: create_release
112+
uses: actions/create-release@v1
113+
env:
114+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
115+
with:
116+
tag_name: v${{ steps.package-version.outputs.VERSION }}
117+
release_name: Release v${{ steps.package-version.outputs.VERSION }}
118+
body: |
119+
## Docker Image
120+
Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.package-version.outputs.VERSION }}
121+
122+
## Pull Request Messages
123+
${{ steps.pr-messages.outputs.PR_MESSAGES }}
124+
125+
## Commit Messages
126+
${{ steps.commit-messages.outputs.COMMIT_MESSAGES }}
127+
128+
## Security Scan
129+
A security scan was performed on this Docker image. Any critical or high vulnerabilities would have prevented this release.
130+
draft: false
131+
prerelease: false

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
node_modules
3+
.env
4+
dist

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"printWidth": 120,
3+
"singleQuote": true,
4+
"tabWidth": 2,
5+
"trailingComma": "none",
6+
"semi": false
7+
}

Dockerfile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Use an official Node.js runtime as the base image
2+
FROM node:18
3+
4+
# Install Nginx
5+
RUN apt-get update && apt-get install -y nginx certbot python3-certbot-nginx
6+
7+
# Create app directory
8+
WORKDIR /usr/src/app
9+
10+
# Bundle app source
11+
COPY . .
12+
13+
RUN yarn && yarn build
14+
15+
# Expose port 80 & 443
16+
EXPOSE 8080 443
17+
18+
# Remove the default Nginx configuration file
19+
RUN rm /etc/nginx/sites-enabled/default
20+
21+
# Copy a custom Nginx configuration file
22+
COPY nginx.conf /etc/nginx/nginx.conf
23+
24+
# Make sure Nginx has the right permissions to run
25+
RUN chown -R www-data:www-data /var/lib/nginx
26+
27+
# Create directory for Let's Encrypt certificates
28+
RUN mkdir -p /etc/letsencrypt
29+
30+
# Start Node.js app
31+
CMD ["node", "dist/index.js"]

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# LLM Proxy
2+
3+
Manages Nginx for reverse proxy to multiple LLMs, with TLS & Bearer Auth tokens. Deployed with docker.
4+
5+
- Aggregates multiple OpenAI-type LLM APIs
6+
- Supports cloudflare domains
7+
- Uses Let's Encrypt for TLS certificates
8+
- Uses certbot for certificate issuance and renewal
9+
- Uses Nginx as a public-domain reverse proxy to add TLS
10+
- Uses JWT for bearer authentication

docker-compose.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: '3.6'
2+
3+
services:
4+
llmp:
5+
image: ghcr.io/j4ys0n/llm-proxy:1.0.0
6+
container_name: llmp
7+
hostname: llmp
8+
restart: unless-stopped
9+
ports:
10+
- 8080:8080
11+
- 443:443
12+
logging:
13+
driver: 'json-file'
14+
options:
15+
max-size: 100m
16+
max-file: '2'

example.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PORT=3000
2+
TARGET_URLS=http://localhost:1234/v1
3+
JWT_SECRET=your-jwt-secret-key-here
4+
AUTH_USERNAME=admin
5+
AUTH_PASSWORD=secure_password

nginx.conf

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
user www-data;
2+
worker_processes auto;
3+
pid /run/nginx.pid;
4+
include /etc/nginx/modules-enabled/*.conf;
5+
6+
events {
7+
worker_connections 1024;
8+
}
9+
10+
http {
11+
sendfile on;
12+
tcp_nopush on;
13+
types_hash_max_size 2048;
14+
include /etc/nginx/mime.types;
15+
default_type application/octet-stream;
16+
17+
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
18+
ssl_prefer_server_ciphers on;
19+
20+
access_log /var/log/nginx/access.log;
21+
error_log /var/log/nginx/error.log;
22+
23+
gzip on;
24+
25+
server {
26+
listen 80;
27+
server_name localhost;
28+
29+
location / {
30+
proxy_pass http://localhost:3000;
31+
proxy_http_version 1.1;
32+
proxy_set_header Upgrade $http_upgrade;
33+
proxy_set_header Connection 'upgrade';
34+
proxy_set_header Host $host;
35+
proxy_cache_bypass $http_upgrade;
36+
}
37+
}
38+
}

package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "llm-proxy",
3+
"version": "1.0.0",
4+
"description": "A Node.js application that manages Nginx",
5+
"main": "dist/index.js",
6+
"scripts": {
7+
"start": "node dist/index.js",
8+
"build": "tsc",
9+
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
10+
"test": "echo \"Error: no test specified\" && exit 1"
11+
},
12+
"keywords": [
13+
"node",
14+
"nginx",
15+
"typescript"
16+
],
17+
"author": "",
18+
"license": "ISC",
19+
"dependencies": {
20+
"axios": "^1.7.2",
21+
"dotenv": "^10.0.0",
22+
"express": "^4.19.2",
23+
"fs-extra": "^11.2.0",
24+
"jsonwebtoken": "^9.0.2"
25+
},
26+
"devDependencies": {
27+
"@types/express": "^4.17.21",
28+
"@types/fs-extra": "^11.0.4",
29+
"@types/jsonwebtoken": "^9.0.2",
30+
"@types/node": "^20.12.7",
31+
"ts-node-dev": "^2.0.0",
32+
"typescript": "^5.4.5"
33+
}
34+
}

src/controllers/auth.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Express, Request, Response } from 'express'
2+
import jwt from 'jsonwebtoken'
3+
import { log } from '../utils/general'
4+
5+
const jwtSecret = process.env.JWT_SECRET || 'your-jwt-secret'
6+
const authUsername = process.env.AUTH_USERNAME || 'admin'
7+
const authPassword = process.env.AUTH_PASSWORD || 'secure_password'
8+
9+
export class AuthController {
10+
private app: Express
11+
12+
constructor({ app }: { app: Express }) {
13+
this.app = app
14+
}
15+
16+
public registerRoutes(): void {
17+
this.app.post('/auth/token', this.getToken.bind(this))
18+
log('info', 'AuthController initialized')
19+
}
20+
21+
private getToken(req: Request, res: Response): void {
22+
const { username, password } = req.body
23+
if (username === authUsername && password === authPassword) {
24+
const token = jwt.sign({ username }, jwtSecret, { algorithm: 'HS256' })
25+
log('info', `token generated for ${username}`)
26+
res.json({ token })
27+
} else {
28+
res.status(401).json({ error: 'Invalid credentials' })
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)