Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ Thumbs.db
.env
.env.local
frontend/package-lock.json
node_modules/
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ A high-performance **Rust (Axum)** + **React (Vite)** admin panel for managing S

### Panel Features

## 📸 Admin Panel Screenshots
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The screenshot section breaks the heading hierarchy: ### Panel Features is immediately followed by a top-level ## 📸 Admin Panel Screenshots, which makes Panel Features effectively empty and can make README navigation/ToC confusing. Consider making the screenshots a subheading under Panel Features (e.g., ####) or removing/renaming Panel Features so heading levels are consistent.

Suggested change
## 📸 Admin Panel Screenshots
#### 📸 Admin Panel Screenshots

Copilot uses AI. Check for mistakes.

### Settings
![Settings](assets/settings.png)

### User Management
![User Management](assets/user-management.png)

### Export Config
![Export Config](assets/export-config.png)


#### Core Features
- 🚀 **High Performance**: Rust backend with Axum framework and async I/O
- 👥 **User Management**: Full CRUD operations for VPN users with traffic quotas
Expand Down
Binary file added assets/export-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/user-management.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 94 additions & 1 deletion frontend/src/components/ConfigPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { configApi, ServerConfig } from '../api/config';

type TabType = 'network' | 'vpn' | 'limits';
type TabType = 'network' | 'vpn' | 'limits' | 'users' | 'export';

interface FormErrors {
[key: string]: string;
Expand Down Expand Up @@ -100,6 +100,8 @@ export function ConfigPanel() {
{ id: 'network' as TabType, label: 'Network' },
{ id: 'vpn' as TabType, label: 'VPN Settings' },
{ id: 'limits' as TabType, label: 'Default Limits' },
{ id: 'users' as TabType, label: 'User Management' },
{ id: 'export' as TabType, label: 'Export Config' },
];

if (loading) {
Expand Down Expand Up @@ -453,6 +455,97 @@ export function ConfigPanel() {
</div>
)}


{/* User Management Tab */}
{activeTab === 'users' && (
<div className="space-y-6">
<div className="flex justify-between items-center">
<h2 className="text-xl font-semibold text-white">VPN Users</h2>
<button type="button" className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium transition-colors">
Add User
</button>
</div>
Comment on lines +462 to +467
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new User Management tab renders several buttons (e.g., "Add User") with no click handler/disabled state. In the running UI this looks like broken functionality. If this tab is intentionally a mock for screenshots, consider marking it as "Coming soon" and disabling the controls, or wire the buttons to placeholder actions (e.g., open a modal) so the UI doesn’t appear non-functional.

Copilot uses AI. Check for mistakes.

<div className="bg-gray-800 rounded-lg overflow-hidden border border-gray-700">
<table className="min-w-full divide-y divide-gray-700">
<thead className="bg-gray-700/50">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Username</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Status</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Traffic Usage</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Connections</th>
<th scope="col" className="px-6 py-3 text-right text-xs font-medium text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-700">
<tr>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">admin</td>
<td className="px-6 py-4 whitespace-nowrap text-sm"><span className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span></td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">1.2 GB / 50 GB</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">1 / 5</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button className="text-blue-400 hover:text-blue-300 mr-3">Edit</button>
<button className="text-red-400 hover:text-red-300">Delete</button>
</td>
Comment on lines +486 to +489
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The per-row action buttons (Edit/Delete) don’t have handlers or disabled state, so they appear clickable but do nothing. Either disable them (and visually indicate they’re placeholders) or implement the intended actions to avoid a UI that feels broken.

Copilot uses AI. Check for mistakes.
</tr>
<tr>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">test_user</td>
<td className="px-6 py-4 whitespace-nowrap text-sm"><span className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Warning</span></td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">48.5 GB / 50 GB</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">0 / 2</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button className="text-blue-400 hover:text-blue-300 mr-3">Edit</button>
<button className="text-red-400 hover:text-red-300">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
)}

{/* Export Config Tab */}
{activeTab === 'export' && (
<div className="space-y-6">
<h2 className="text-xl font-semibold text-white mb-4">Export Configurations</h2>

<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-lg font-medium text-white mb-2">SlipNet Encrypted Connection</h3>
<p className="text-sm text-gray-400 mb-4">Export the full encrypted connection string for the Slipstream client.</p>

<div className="flex items-center space-x-4 mb-6">
<input type="text" readOnly value="slipnet-enc://eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYy1rZXkifQ.eyJzZXJ2ZXIiOiIx..." className="flex-1 bg-gray-900 border border-gray-600 rounded px-4 py-2 text-gray-300 font-mono text-sm" />
<button type="button" className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors">Copy</button>
</div>

<h3 className="text-lg font-medium text-white mb-2">QR Code Configuration</h3>
<p className="text-sm text-gray-400 mb-4">Scan this QR code with the mobile app to automatically configure your connection.</p>

<div className="flex justify-center p-6 bg-white rounded-lg inline-block">
<div className="w-48 h-48 bg-gray-200 border-4 border-black flex items-center justify-center relative overflow-hidden">
<div className="w-full h-full flex flex-wrap">
{/* Simulated QR code pattern */}
{Array.from({length: 100}).map((_, i) => (
<div key={i} className={`w-[10%] h-[10%] ${Math.random() > 0.5 ? 'bg-black' : 'bg-transparent'}`}></div>
))}
Comment on lines +527 to +530
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This QR "simulation" uses Math.random() during render, so the pattern (and DOM) changes on every re-render (typing in any config field, toggling tabs, React StrictMode double render, etc.). This causes unnecessary work, visual flicker, and can create hydration mismatches if the app is ever SSR’d. Prefer a deterministic pattern (e.g., generate once with useMemo/state using a seeded RNG), or render a real QR code from the export string using a QR library.

Copilot uses AI. Check for mistakes.
</div>
<div className="absolute inset-0 border-[10px] border-black m-2"></div>
<div className="absolute inset-0 border-[10px] border-black m-2" style={{clipPath: 'polygon(0 0, 100% 0, 100% 25%, 25% 25%, 25% 100%, 0 100%)'}}></div>
<div className="absolute w-8 h-8 bg-black m-4 top-0 left-0"></div>
<div className="absolute w-8 h-8 bg-black m-4 top-0 right-0"></div>
<div className="absolute w-8 h-8 bg-black m-4 bottom-0 left-0"></div>
</div>
</div>

<div className="mt-8">
<button type="button" className="px-4 py-2 border border-gray-600 hover:bg-gray-700 text-white rounded transition-colors">
Download JSON Config
</button>
Comment on lines +516 to +543
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Export Config tab includes interactive controls ("Copy", "Download JSON Config") without handlers, so they appear functional but do nothing. Consider implementing copy/download behavior (or disable + label them as placeholders) so the UI doesn’t present broken actions.

Copilot uses AI. Check for mistakes.
</div>
</div>
</div>
)}

{/* Save Button */}
<div className="mt-8 pt-6 border-t border-gray-700">
<button
Expand Down
Loading