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 # Copy this file to .env and fill in your values
# ============================================================================== # ==============================================================================

View File

@@ -46,4 +46,4 @@ jobs:
go-version: '1.22' go-version: '1.22'
cache: true cache: true
- name: Build - 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) # 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 ## Core Technologies
@@ -15,7 +15,7 @@ The LLM Proxy backend is implemented in Go, focusing on high performance, clear
```text ```text
├── cmd/ ├── cmd/
│ └── llm-proxy/ # Entry point (main.go) │ └── gophergate/ # Entry point (main.go)
├── internal/ ├── internal/
│ ├── config/ # Configuration loading and validation │ ├── config/ # Configuration loading and validation
│ ├── db/ # Database schema, migrations, and models │ ├── db/ # Database schema, migrations, and models

View File

@@ -11,7 +11,7 @@ RUN go mod download
COPY . . COPY . .
# Build the application # 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 # Final stage
FROM alpine:latest FROM alpine:latest
@@ -21,7 +21,7 @@ RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app WORKDIR /app
# Copy the binary from the builder stage # Copy the binary from the builder stage
COPY --from=builder /app/llm-proxy . COPY --from=builder /app/gophergate .
COPY --from=builder /app/static ./static COPY --from=builder /app/static ./static
# Create data directory # Create data directory
@@ -31,4 +31,4 @@ RUN mkdir -p /app/data
EXPOSE 8080 EXPOSE 8080
# Run the application # 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. 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 ## 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. - **Signed Session Tokens:** Management dashboard sessions are secured using HMAC-SHA256 signed tokens.
- **Encrypted Storage:** Support for encrypted provider API keys in the database. - **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: 1. Clone and build:
```bash ```bash
git clone <repository-url> git clone <repository-url>
cd llm-proxy cd gophergate
go build -o llm-proxy ./cmd/llm-proxy go build -o gophergate ./cmd/gophergate
``` ```
2. Configure environment: 2. Configure environment:
@@ -70,7 +70,7 @@ LLM Proxy is designed with security in mind:
3. Run the proxy: 3. Run the proxy:
```bash ```bash
./llm-proxy ./gophergate
``` ```
The server starts on `http://0.0.0.0:8080` by default. 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 ```bash
# Build the container # Build the container
docker build -t llm-proxy . docker build -t gophergate .
# Run the container # Run the container
docker run -p 8080:8080 \ docker run -p 8080:8080 \
-e LLM_PROXY__ENCRYPTION_KEY=your-secure-key \ -e LLM_PROXY__ENCRYPTION_KEY=your-secure-key \
-v ./data:/app/data \ -v ./data:/app/data \
llm-proxy gophergate
``` ```
## Management Dashboard ## Management Dashboard
@@ -107,7 +107,7 @@ Access the dashboard at `http://localhost:8080`.
**Forgot Password?** **Forgot Password?**
You can reset the admin password to default by running: You can reset the admin password to default by running:
```bash ```bash
./llm-proxy -reset-admin ./gophergate -reset-admin
``` ```
## API Usage ## API Usage

View File

@@ -5,9 +5,9 @@ import (
"log" "log"
"os" "os"
"llm-proxy/internal/config" "gophergate/internal/config"
"llm-proxy/internal/db" "gophergate/internal/db"
"llm-proxy/internal/server" "gophergate/internal/server"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
@@ -48,7 +48,7 @@ func main() {
s := server.NewServer(cfg, database) s := server.NewServer(cfg, database)
// Run server // 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 { if err := s.Run(); err != nil {
log.Fatalf("Server failed: %v", err) log.Fatalf("Server failed: %v", err)
} }

View File

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

4
go.mod
View File

@@ -1,4 +1,4 @@
module llm-proxy module gophergate
go 1.26.1 go 1.26.1
@@ -9,6 +9,7 @@ require (
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/viper v1.21.0 github.com/spf13/viper v1.21.0
golang.org/x/crypto v0.48.0 golang.org/x/crypto v0.48.0
modernc.org/sqlite v1.47.0 modernc.org/sqlite v1.47.0
@@ -44,7 +45,6 @@ require (
github.com/quic-go/quic-go v0.59.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sagikazarmark/locafero v0.11.0 // 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/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // 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/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 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 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 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= 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= 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" "log"
"strings" "strings"
"llm-proxy/internal/db" "gophergate/internal/db"
"llm-proxy/internal/models" "gophergate/internal/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -148,22 +148,54 @@ body {
width: 80px; width: 80px;
height: 80px; height: 80px;
margin: 0 auto 1.25rem; margin: 0 auto 1.25rem;
border-radius: 16px;
background: var(--bg2);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: var(--orange); background: rgba(254, 128, 25, 0.15);
font-size: 2rem; 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); 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 { .login-header h1 {
font-size: 1.75rem; font-size: 2rem;
font-weight: 800; font-weight: 800;
color: var(--fg0); color: var(--primary-light);
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
letter-spacing: -0.025em; letter-spacing: -0.025em;
text-transform: uppercase;
} }
.login-subtitle { .login-subtitle {

View File

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

View File

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