chore: rebrand project to GopherGate
Some checks failed
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled

Updated all naming from LLM Proxy to GopherGate. Implemented new CSS-based branding and updated Go module/binary naming.
This commit is contained in:
2026-03-19 13:37:05 -04:00
parent 0ce5f4f490
commit 03dca998df
25 changed files with 195 additions and 169 deletions

View File

@@ -1,4 +1,4 @@
# LLM Proxy Gateway Configuration Example
# GopherGate Configuration Example
# Copy this file to .env and fill in your values
# ==============================================================================

View File

@@ -46,4 +46,4 @@ jobs:
go-version: '1.22'
cache: true
- name: Build
run: go build -v -o llm-proxy ./cmd/llm-proxy
run: go build -v -o gophergate ./cmd/gophergate

View File

@@ -1,6 +1,6 @@
# Backend Architecture (Go)
The LLM Proxy backend is implemented in Go, focusing on high performance, clear concurrency patterns, and maintainability.
The GopherGate backend is implemented in Go, focusing on high performance, clear concurrency patterns, and maintainability.
## Core Technologies
@@ -15,7 +15,7 @@ The LLM Proxy backend is implemented in Go, focusing on high performance, clear
```text
├── cmd/
│ └── llm-proxy/ # Entry point (main.go)
│ └── gophergate/ # Entry point (main.go)
├── internal/
│ ├── config/ # Configuration loading and validation
│ ├── db/ # Database schema, migrations, and models

View File

@@ -11,7 +11,7 @@ RUN go mod download
COPY . .
# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -o llm-proxy ./cmd/llm-proxy
RUN CGO_ENABLED=0 GOOS=linux go build -o gophergate ./cmd/gophergate
# Final stage
FROM alpine:latest
@@ -21,7 +21,7 @@ RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
# Copy the binary from the builder stage
COPY --from=builder /app/llm-proxy .
COPY --from=builder /app/gophergate .
COPY --from=builder /app/static ./static
# Create data directory
@@ -31,4 +31,4 @@ RUN mkdir -p /app/data
EXPOSE 8080
# Run the application
CMD ["./llm-proxy"]
CMD ["./gophergate"]

View File

@@ -1,4 +1,4 @@
# LLM Proxy Gateway
# GopherGate
A unified, high-performance LLM proxy gateway built in Go. It provides a single OpenAI-compatible API to access multiple providers (OpenAI, Gemini, DeepSeek, Grok, Ollama) with built-in token tracking, real-time cost calculation, multi-user authentication, and a management dashboard.
@@ -27,7 +27,7 @@ A unified, high-performance LLM proxy gateway built in Go. It provides a single
## Security
LLM Proxy is designed with security in mind:
GopherGate is designed with security in mind:
- **Signed Session Tokens:** Management dashboard sessions are secured using HMAC-SHA256 signed tokens.
- **Encrypted Storage:** Support for encrypted provider API keys in the database.
@@ -55,8 +55,8 @@ LLM Proxy is designed with security in mind:
1. Clone and build:
```bash
git clone <repository-url>
cd llm-proxy
go build -o llm-proxy ./cmd/llm-proxy
cd gophergate
go build -o gophergate ./cmd/gophergate
```
2. Configure environment:
@@ -70,7 +70,7 @@ LLM Proxy is designed with security in mind:
3. Run the proxy:
```bash
./llm-proxy
./gophergate
```
The server starts on `http://0.0.0.0:8080` by default.
@@ -79,13 +79,13 @@ The server starts on `http://0.0.0.0:8080` by default.
```bash
# Build the container
docker build -t llm-proxy .
docker build -t gophergate .
# Run the container
docker run -p 8080:8080 \
-e LLM_PROXY__ENCRYPTION_KEY=your-secure-key \
-v ./data:/app/data \
llm-proxy
gophergate
```
## Management Dashboard
@@ -107,7 +107,7 @@ Access the dashboard at `http://localhost:8080`.
**Forgot Password?**
You can reset the admin password to default by running:
```bash
./llm-proxy -reset-admin
./gophergate -reset-admin
```
## API Usage

View File

@@ -5,9 +5,9 @@ import (
"log"
"os"
"llm-proxy/internal/config"
"llm-proxy/internal/db"
"llm-proxy/internal/server"
"gophergate/internal/config"
"gophergate/internal/db"
"gophergate/internal/server"
"github.com/joho/godotenv"
"golang.org/x/crypto/bcrypt"
@@ -48,7 +48,7 @@ func main() {
s := server.NewServer(cfg, database)
// Run server
log.Printf("Starting LLM Proxy on %s:%d", cfg.Server.Host, cfg.Server.Port)
log.Printf("Starting GopherGate on %s:%d", cfg.Server.Host, cfg.Server.Port)
if err := s.Run(); err != nil {
log.Fatalf("Server failed: %v", err)
}

View File

@@ -1,6 +1,6 @@
# Deployment Guide (Go)
This guide covers deploying the Go-based LLM Proxy Gateway.
This guide covers deploying the Go-based GopherGate.
## Environment Setup
@@ -18,12 +18,12 @@ This guide covers deploying the Go-based LLM Proxy Gateway.
### 1. Build
```bash
go build -o llm-proxy ./cmd/llm-proxy
go build -o gophergate ./cmd/gophergate
```
### 2. Run
```bash
./llm-proxy
./gophergate
```
## Docker Deployment
@@ -32,17 +32,17 @@ The project includes a multi-stage `Dockerfile` for minimal image size.
### 1. Build Image
```bash
docker build -t llm-proxy .
docker build -t gophergate .
```
### 2. Run Container
```bash
docker run -d \
--name llm-proxy \
--name gophergate \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
--env-file .env \
llm-proxy
gophergate
```
## Production Considerations

4
go.mod
View File

@@ -1,4 +1,4 @@
module llm-proxy
module gophergate
go 1.26.1
@@ -9,6 +9,7 @@ require (
github.com/gorilla/websocket v1.5.3
github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/viper v1.21.0
golang.org/x/crypto v0.48.0
modernc.org/sqlite v1.47.0
@@ -44,7 +45,6 @@ require (
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect

2
go.sum
View File

@@ -104,6 +104,8 @@ github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/i
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=

BIN
gophergate Executable file

Binary file not shown.

View File

@@ -4,8 +4,8 @@ import (
"log"
"strings"
"llm-proxy/internal/db"
"llm-proxy/internal/models"
"gophergate/internal/db"
"gophergate/internal/models"
"github.com/gin-gonic/gin"
)

View File

@@ -8,8 +8,8 @@ import (
"io"
"strings"
"llm-proxy/internal/config"
"llm-proxy/internal/models"
"gophergate/internal/config"
"gophergate/internal/models"
"github.com/go-resty/resty/v2"
)

View File

@@ -5,8 +5,8 @@ import (
"encoding/json"
"fmt"
"llm-proxy/internal/config"
"llm-proxy/internal/models"
"gophergate/internal/config"
"gophergate/internal/models"
"github.com/go-resty/resty/v2"
)

View File

@@ -5,8 +5,8 @@ import (
"encoding/json"
"fmt"
"llm-proxy/internal/config"
"llm-proxy/internal/models"
"gophergate/internal/config"
"gophergate/internal/models"
"github.com/go-resty/resty/v2"
)

View File

@@ -7,7 +7,7 @@ import (
"io"
"strings"
"llm-proxy/internal/models"
"gophergate/internal/models"
)
// MessagesToOpenAIJSON converts unified messages to OpenAI-compatible JSON, including tools and images.

View File

@@ -6,8 +6,8 @@ import (
"fmt"
"strings"
"llm-proxy/internal/config"
"llm-proxy/internal/models"
"gophergate/internal/config"
"gophergate/internal/models"
"github.com/go-resty/resty/v2"
)

View File

@@ -3,7 +3,7 @@ package providers
import (
"context"
"llm-proxy/internal/models"
"gophergate/internal/models"
)
type Provider interface {

View File

@@ -8,8 +8,8 @@ import (
"strings"
"time"
"llm-proxy/internal/db"
"llm-proxy/internal/models"
"gophergate/internal/db"
"gophergate/internal/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"

View File

@@ -4,7 +4,7 @@ import (
"log"
"time"
"llm-proxy/internal/db"
"gophergate/internal/db"
)
type RequestLog struct {

View File

@@ -8,12 +8,12 @@ import (
"strings"
"time"
"llm-proxy/internal/config"
"llm-proxy/internal/db"
"llm-proxy/internal/middleware"
"llm-proxy/internal/models"
"llm-proxy/internal/providers"
"llm-proxy/internal/utils"
"gophergate/internal/config"
"gophergate/internal/db"
"gophergate/internal/middleware"
"gophergate/internal/models"
"gophergate/internal/providers"
"gophergate/internal/utils"
"github.com/gin-gonic/gin"
)

View File

@@ -90,7 +90,7 @@ func (s *Server) handleWebSocket(c *gin.Context) {
// Initial message
conn.WriteJSON(gin.H{
"type": "connected",
"message": "Connected to LLM Proxy Dashboard",
"message": "Connected to GopherGate Dashboard",
})
for {

View File

@@ -6,7 +6,7 @@ import (
"log"
"time"
"llm-proxy/internal/models"
"gophergate/internal/models"
"github.com/go-resty/resty/v2"
)

View File

@@ -148,22 +148,54 @@ body {
width: 80px;
height: 80px;
margin: 0 auto 1.25rem;
border-radius: 16px;
background: var(--bg2);
display: flex;
align-items: center;
justify-content: center;
color: var(--orange);
font-size: 2rem;
background: rgba(254, 128, 25, 0.15);
color: var(--primary);
border-radius: 12px;
font-size: 2.5rem;
}
/* GopherGate Logo Icon */
.logo-icon-container {
width: 60px;
height: 60px;
background: var(--blue-light);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow);
border: 2px solid var(--fg1);
margin: 0 auto;
}
.logo-icon-container.small {
width: 32px;
height: 32px;
border-radius: 6px;
margin: 0;
}
.logo-icon-text {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
color: var(--bg0);
font-size: 1.8rem;
}
.logo-icon-container.small .logo-icon-text {
font-size: 1rem;
}
.login-header h1 {
font-size: 1.75rem;
font-size: 2rem;
font-weight: 800;
color: var(--fg0);
color: var(--primary-light);
margin-bottom: 0.5rem;
letter-spacing: -0.025em;
text-transform: uppercase;
}
.login-subtitle {

View File

@@ -3,50 +3,38 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Proxy Gateway - Admin Dashboard</title>
<title>GopherGate - Admin Dashboard</title>
<link rel="stylesheet" href="/css/dashboard.css?v=11">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="icon" href="img/logo-icon.png" type="image/png" sizes="any">
<link rel="apple-touch-icon" href="img/logo-icon.png">
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/global/luxon.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<!-- Login Screen -->
<div id="login-screen" class="login-container">
<div class="login-card">
<div class="login-header">
<i class="fas fa-terminal login-logo-fallback"></i>
<h1>LLM Proxy Gateway</h1>
<p class="login-subtitle">Admin Dashboard</p>
<body class="gruvbox-dark">
<!-- Auth Page -->
<div id="auth-page" class="auth-container">
<div class="auth-card">
<div class="auth-header">
<div class="auth-logo">
<div class="logo-icon-container">
<span class="logo-icon-text">GG</span>
</div>
</div>
<h1>GopherGate</h1>
<p>Secure LLM Gateway & Management</p>
</div>
<form id="login-form" class="login-form" onsubmit="event.preventDefault();">
<div class="form-group">
<input type="text" id="username" name="username" placeholder=" " required>
<label for="username">
<i class="fas fa-user"></i> Username
</label>
<form id="login-form">
<div class="form-control">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autocomplete="username">
</div>
<div class="form-group">
<input type="password" id="password" name="password" placeholder=" " required>
<label for="password">
<i class="fas fa-lock"></i> Password
</label>
</div>
<div class="form-group">
<button type="submit" class="login-btn">
<i class="fas fa-sign-in-alt"></i> Sign In
</button>
</div>
<div class="login-footer">
<p>Default: <code>admin</code> / <code>admin</code> (change in Settings &gt; Security)</p>
<div class="form-control">
<label for="password">Password</label>
<input type="password" id="password" name="password" required autocomplete="current-password">
</div>
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</form>
<div id="login-error" class="error-message" style="display: none;">
<i class="fas fa-exclamation-circle"></i>
<span>Invalid credentials. Please try again.</span>
</div>
<div id="login-error" class="error-message" style="display: none;"></div>
</div>
</div>
@@ -56,9 +44,10 @@
<nav class="sidebar">
<div class="sidebar-header">
<div class="logo">
<img src="img/logo-icon.png" alt="LLM Proxy" class="sidebar-logo" onerror="this.style.display='none'; this.nextElementSibling.style.display='inline-block';">
<i class="fas fa-shield-alt logo-fallback" style="display: none;"></i>
<span>LLM Proxy</span>
<div class="logo-icon-container small">
<span class="logo-icon-text">GG</span>
</div>
<span>GopherGate</span>
</div>
<button class="sidebar-toggle" id="sidebar-toggle">
<i class="fas fa-bars"></i>
@@ -68,68 +57,74 @@
<div class="sidebar-menu">
<div class="menu-section">
<h3 class="menu-title">MAIN</h3>
<a href="#overview" class="menu-item active" data-page="overview" data-tooltip="Dashboard Overview">
<i class="fas fa-th-large"></i>
<span>Overview</span>
</a>
<a href="#analytics" class="menu-item" data-page="analytics" data-tooltip="Usage Analytics">
<i class="fas fa-chart-line"></i>
<span>Analytics</span>
</a>
<a href="#costs" class="menu-item" data-page="costs" data-tooltip="Cost Tracking">
<i class="fas fa-dollar-sign"></i>
<span>Cost Management</span>
</a>
<ul class="menu-list">
<li class="menu-item active" data-page="overview">
<i class="fas fa-th-large"></i>
<span>Overview</span>
</li>
<li class="menu-item" data-page="analytics">
<i class="fas fa-chart-bar"></i>
<span>Analytics</span>
</li>
<li class="menu-item" data-page="costs">
<i class="fas fa-dollar-sign"></i>
<span>Costs & Billing</span>
</li>
</ul>
</div>
<div class="menu-section">
<h3 class="menu-title">MANAGEMENT</h3>
<a href="#clients" class="menu-item" data-page="clients" data-tooltip="API Clients">
<i class="fas fa-users"></i>
<span>Client Management</span>
</a>
<a href="#providers" class="menu-item" data-page="providers" data-tooltip="Model Providers">
<i class="fas fa-server"></i>
<span>Providers</span>
</a>
<a href="#models" class="menu-item" data-page="models" data-tooltip="Manage Models">
<i class="fas fa-cube"></i>
<span>Models</span>
</a>
<a href="#monitoring" class="menu-item" data-page="monitoring" data-tooltip="Live Monitoring">
<i class="fas fa-heartbeat"></i>
<span>Real-time Monitoring</span>
</a>
<ul class="menu-list">
<li class="menu-item" data-page="clients">
<i class="fas fa-users"></i>
<span>Clients</span>
</li>
<li class="menu-item" data-page="providers">
<i class="fas fa-server"></i>
<span>Providers</span>
</li>
<li class="menu-item" data-page="models">
<i class="fas fa-brain"></i>
<span>Models</span>
</li>
</ul>
</div>
<div class="menu-section">
<h3 class="menu-title">SYSTEM</h3>
<a href="#users" class="menu-item admin-only" data-page="users" data-tooltip="User Accounts">
<i class="fas fa-user-shield"></i>
<span>User Management</span>
</a>
<a href="#settings" class="menu-item admin-only" data-page="settings" data-tooltip="System Settings">
<i class="fas fa-cog"></i>
<span>Settings</span>
</a>
<a href="#logs" class="menu-item" data-page="logs" data-tooltip="System Logs">
<i class="fas fa-list-alt"></i>
<span>System Logs</span>
</a>
<ul class="menu-list">
<li class="menu-item" data-page="monitoring">
<i class="fas fa-activity"></i>
<span>Live Monitoring</span>
</li>
<li class="menu-item" data-page="logs">
<i class="fas fa-list-alt"></i>
<span>Logs</span>
</li>
<li class="menu-item" data-page="users">
<i class="fas fa-user-shield"></i>
<span>Admin Users</span>
</li>
<li class="menu-item" data-page="settings">
<i class="fas fa-cog"></i>
<span>Settings</span>
</li>
</ul>
</div>
</div>
<div class="sidebar-footer">
<div class="user-info">
<div class="user-avatar">
<i class="fas fa-user-circle"></i>
<i class="fas fa-user"></i>
</div>
<div class="user-details">
<span class="user-name">Loading...</span>
<span class="user-role">...</span>
<div class="user-name" id="display-username">Admin</div>
<div class="user-role" id="display-role">Administrator</div>
</div>
</div>
<button class="logout-btn" id="logout-btn" title="Logout">
<button id="logout-btn" class="btn-icon" title="Logout">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
@@ -137,43 +132,40 @@
<!-- Main Content -->
<main class="main-content">
<!-- Top Navigation -->
<header class="top-nav">
<div class="nav-left">
<h1 class="page-title" id="page-title">Dashboard Overview</h1>
<header class="top-bar">
<div class="page-title">
<h2 id="current-page-title">Overview</h2>
</div>
<div class="nav-right">
<div class="nav-item" id="ws-status-nav" title="WebSocket Connection Status">
<div class="ws-dot"></div>
<span class="ws-text">Connecting...</span>
<div class="top-bar-actions">
<div id="connection-status" class="status-indicator">
<span class="status-dot"></span>
<span class="status-text">Disconnected</span>
</div>
<div class="nav-item" title="Refresh Current Page">
<i class="fas fa-sync-alt" id="refresh-btn"></i>
</div>
<div class="nav-item">
<span id="current-time">Loading...</span>
<div class="theme-toggle" id="theme-toggle">
<i class="fas fa-moon"></i>
</div>
</div>
</header>
<!-- Page Content -->
<div class="page-content" id="page-content">
<!-- Dynamic content container -->
</div>
<!-- Global Spinner -->
<div class="spinner-container">
<div class="spinner"></div>
<div id="page-content" class="content-body">
<!-- Content will be loaded dynamically -->
<div class="loader-container">
<div class="loader"></div>
</div>
</div>
</main>
</div>
<!-- Scripts (cache-busted with version query params) -->
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js"></script>
<script src="/js/api.js?v=7"></script>
<script src="/js/auth.js?v=7"></script>
<script src="/js/dashboard.js?v=7"></script>
<script src="/js/websocket.js?v=7"></script>
<script src="/js/charts.js?v=7"></script>
<script src="/js/websocket.js?v=7"></script>
<script src="/js/dashboard.js?v=7"></script>
<!-- Page Modules -->
<script src="/js/pages/overview.js?v=7"></script>
<script src="/js/pages/analytics.js?v=7"></script>
<script src="/js/pages/costs.js?v=7"></script>
@@ -185,4 +177,4 @@
<script src="/js/pages/logs.js?v=7"></script>
<script src="/js/pages/users.js?v=7"></script>
</body>
</html>
</html>

View File

@@ -1,4 +1,4 @@
// Authentication Module for LLM Proxy Dashboard
// Authentication Module for GopherGate Dashboard
class AuthManager {
constructor() {