mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-23 08:49:08 +00:00
feat: added nav
This commit is contained in:
1
client/src/components/index.ts
Normal file
1
client/src/components/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './nav-menu';
|
1
client/src/components/nav-menu/index.ts
Normal file
1
client/src/components/nav-menu/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './nav-menu';
|
31
client/src/components/nav-menu/nav-menu.tsx
Normal file
31
client/src/components/nav-menu/nav-menu.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Link, useLocation } from 'react-router';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
interface NavMenuItems {
|
||||
href: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface NavMenuProps {
|
||||
items: NavMenuItems[];
|
||||
}
|
||||
|
||||
export const NavMenu = (props: NavMenuProps) => {
|
||||
const { items } = props;
|
||||
const { pathname } = useLocation();
|
||||
|
||||
return (
|
||||
<div className="tabs">
|
||||
{items.map(({ href, label }) => (
|
||||
<Link
|
||||
className={clsx('tab', {
|
||||
active: href === pathname,
|
||||
})}
|
||||
to={href}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
52
client/src/pages/certificates/certificates.tsx
Normal file
52
client/src/pages/certificates/certificates.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const Certificates = () => {
|
||||
return (
|
||||
<div id="certificates" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Add Certificate</h2>
|
||||
<form id="certificateForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="certName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Domain:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="certDomain"
|
||||
placeholder="example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Certificate Type:</label>
|
||||
<select id="certType" required>
|
||||
<option value="self_signed">Self-Signed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="certAutoRenew" checked /> Auto Renew
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Generate Certificate
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Certificates List</h2>
|
||||
<div id="certificatesList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CertificatesRoute: RouteObject = {
|
||||
path: '/certificates',
|
||||
Component: Certificates,
|
||||
};
|
1
client/src/pages/certificates/index.ts
Normal file
1
client/src/pages/certificates/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './certificates';
|
29
client/src/pages/dashboard/dashboard.tsx
Normal file
29
client/src/pages/dashboard/dashboard.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const Dashboard = () => {
|
||||
return (
|
||||
<div id="dashboard" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Statistics</h2>
|
||||
<p>
|
||||
Servers: <span id="serverCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Templates: <span id="templateCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Certificates: <span id="certCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Users: <span id="userCount">Loading...</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DashboardRoute: RouteObject = {
|
||||
index: true,
|
||||
path: '/',
|
||||
Component: Dashboard,
|
||||
};
|
1
client/src/pages/dashboard/index.ts
Normal file
1
client/src/pages/dashboard/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './dashboard';
|
@@ -1,5 +1,7 @@
|
||||
import { Outlet } from 'react-router';
|
||||
import './home.css';
|
||||
import { NavMenu } from '../../components/nav-menu/nav-menu';
|
||||
import { navItems } from './utils';
|
||||
|
||||
export const Home = () => {
|
||||
return (
|
||||
@@ -10,266 +12,21 @@ export const Home = () => {
|
||||
{/* <!-- Toast notifications container --> */}
|
||||
<div className="toast-container" id="toastContainer"></div>
|
||||
|
||||
<div className="tabs">
|
||||
<div
|
||||
className="tab active"
|
||||
// onClick="showTab('dashboard')"
|
||||
>
|
||||
Dashboard
|
||||
</div>
|
||||
<div
|
||||
className="tab"
|
||||
// onClick="showTab('servers')"
|
||||
>
|
||||
Servers
|
||||
</div>
|
||||
<div
|
||||
className="tab"
|
||||
//onClick="showTab('templates')"
|
||||
>
|
||||
Inbound Templates
|
||||
</div>
|
||||
<div
|
||||
className="tab"
|
||||
// onClick="showTab('certificates')"
|
||||
>
|
||||
Certificates
|
||||
</div>
|
||||
<div
|
||||
className="tab"
|
||||
// onClick="showTab('inbounds')"
|
||||
>
|
||||
Inbound Binding
|
||||
</div>
|
||||
<div
|
||||
className="tab"
|
||||
// onClick="showTab('users')"
|
||||
>
|
||||
Users
|
||||
</div>
|
||||
</div>
|
||||
<NavMenu items={navItems} />
|
||||
|
||||
{/* <!-- Dashboard --> */}
|
||||
<div id="dashboard" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Statistics</h2>
|
||||
<p>
|
||||
Servers: <span id="serverCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Templates: <span id="templateCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Certificates: <span id="certCount">Loading...</span>
|
||||
</p>
|
||||
<p>
|
||||
Users: <span id="userCount">Loading...</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Outlet />
|
||||
|
||||
{/* <!-- Servers --> */}
|
||||
<div id="servers" className="tab-content">
|
||||
<div className="section">
|
||||
<h2>Add Server</h2>
|
||||
<form id="serverForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="serverName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Hostname:</label>
|
||||
<input type="text" id="serverHostname" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>gRPC Port:</label>
|
||||
<input type="number" id="serverPort" value="2053" />
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add Server
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Servers List</h2>
|
||||
<div id="serversList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <!-- Templates --> */}
|
||||
<div id="templates" className="tab-content">
|
||||
<div className="section">
|
||||
<h2>Add Template</h2>
|
||||
<form id="templateForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="templateName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Protocol:</label>
|
||||
<select id="templateProtocol" required>
|
||||
<option value="vless">VLESS</option>
|
||||
<option value="vmess">VMess</option>
|
||||
<option value="trojan">Trojan</option>
|
||||
<option value="shadowsocks">Shadowsocks</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Default Port:</label>
|
||||
<input type="number" id="templatePort" value="443" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="templateTls" /> Requires TLS
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Configuration Template:</label>
|
||||
<textarea
|
||||
id="templateConfig"
|
||||
rows={6}
|
||||
style={{
|
||||
width: '300px',
|
||||
}}
|
||||
></textarea>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add Template
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Templates List</h2>
|
||||
<div id="templatesList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <!-- Certificates --> */}
|
||||
<div id="certificates" className="tab-content">
|
||||
<div className="section">
|
||||
<h2>Add Certificate</h2>
|
||||
<form id="certificateForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="certName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Domain:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="certDomain"
|
||||
placeholder="example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Certificate Type:</label>
|
||||
<select id="certType" required>
|
||||
<option value="self_signed">Self-Signed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="certAutoRenew" checked /> Auto
|
||||
Renew
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Generate Certificate
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Certificates List</h2>
|
||||
<div id="certificatesList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <!-- Server Inbounds --> */}
|
||||
<div id="inbounds" className="tab-content">
|
||||
<div className="section">
|
||||
<h2>Bind Template to Server</h2>
|
||||
<form id="inboundForm">
|
||||
<div className="form-group">
|
||||
<label>Server:</label>
|
||||
<select id="inboundServer" required>
|
||||
<option value="">Select Server...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Template:</label>
|
||||
<select id="inboundTemplate" required>
|
||||
<option value="">Select Template...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Port:</label>
|
||||
<input type="number" id="inboundPort" value="443" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Certificate:</label>
|
||||
<select id="inboundCertificate">
|
||||
<option value="">No Certificate</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="inboundActive" checked /> Active
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Bind Template
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Server Inbounds</h2>
|
||||
<div id="inboundsList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <!-- Users --> */}
|
||||
<div id="users" className="tab-content">
|
||||
<div className="section">
|
||||
<h2>Add User</h2>
|
||||
<form id="userForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="userName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Comment:</label>
|
||||
<input type="text" id="userComment" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Telegram ID:</label>
|
||||
<input type="number" id="userTelegram" />
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add User
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Users List</h2>
|
||||
<div id="usersList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <!-- Modal dialogs --> */}
|
||||
@@ -333,7 +90,6 @@ export const Home = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
26
client/src/pages/home/utils.ts
Normal file
26
client/src/pages/home/utils.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const navItems = [
|
||||
{
|
||||
href: '/',
|
||||
label: 'Dashboard',
|
||||
},
|
||||
{
|
||||
href: '/servers',
|
||||
label: 'Servers',
|
||||
},
|
||||
{
|
||||
href: '/inbound-templates',
|
||||
label: 'Inbound Templates',
|
||||
},
|
||||
{
|
||||
href: '/certificates',
|
||||
label: 'Certificates',
|
||||
},
|
||||
{
|
||||
href: '/inbound-binding',
|
||||
label: 'Inbound Binding',
|
||||
},
|
||||
{
|
||||
href: '/users',
|
||||
label: 'Users',
|
||||
},
|
||||
];
|
55
client/src/pages/inbound-binding/inbound-binding.tsx
Normal file
55
client/src/pages/inbound-binding/inbound-binding.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const InboundBinding = () => {
|
||||
return (
|
||||
<div id="inbounds" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Bind Template to Server</h2>
|
||||
<form id="inboundForm">
|
||||
<div className="form-group">
|
||||
<label>Server:</label>
|
||||
<select id="inboundServer" required>
|
||||
<option value="">Select Server...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Template:</label>
|
||||
<select id="inboundTemplate" required>
|
||||
<option value="">Select Template...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Port:</label>
|
||||
<input type="number" id="inboundPort" value="443" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Certificate:</label>
|
||||
<select id="inboundCertificate">
|
||||
<option value="">No Certificate</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="inboundActive" checked /> Active
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Bind Template
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Server Inbounds</h2>
|
||||
<div id="inboundsList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const InboundBindingRoute: RouteObject = {
|
||||
path: '/inbound-binding',
|
||||
Component: InboundBinding,
|
||||
};
|
1
client/src/pages/inbound-binding/index.ts
Normal file
1
client/src/pages/inbound-binding/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './inbound-binding';
|
60
client/src/pages/inbound-templates/inboud-templates.tsx
Normal file
60
client/src/pages/inbound-templates/inboud-templates.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const InboundTemplates = () => {
|
||||
return (
|
||||
<div id="templates" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Add Template</h2>
|
||||
<form id="templateForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="templateName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Protocol:</label>
|
||||
<select id="templateProtocol" required>
|
||||
<option value="vless">VLESS</option>
|
||||
<option value="vmess">VMess</option>
|
||||
<option value="trojan">Trojan</option>
|
||||
<option value="shadowsocks">Shadowsocks</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Default Port:</label>
|
||||
<input type="number" id="templatePort" value="443" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="templateTls" /> Requires TLS
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Configuration Template:</label>
|
||||
<textarea
|
||||
id="templateConfig"
|
||||
rows={6}
|
||||
style={{
|
||||
width: '300px',
|
||||
}}
|
||||
></textarea>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add Template
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Templates List</h2>
|
||||
<div id="templatesList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const InboundTemplatesRoute: RouteObject = {
|
||||
path: '/inbound-templates',
|
||||
Component: InboundTemplates,
|
||||
};
|
1
client/src/pages/inbound-templates/index.ts
Normal file
1
client/src/pages/inbound-templates/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './inboud-templates';
|
@@ -1 +1,7 @@
|
||||
export * from './home';
|
||||
export * from './certificates';
|
||||
export * from './dashboard';
|
||||
export * from './inbound-binding';
|
||||
export * from './inbound-templates';
|
||||
export * from './servers';
|
||||
export * from './users';
|
||||
|
1
client/src/pages/servers/index.ts
Normal file
1
client/src/pages/servers/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './servers';
|
40
client/src/pages/servers/servers.tsx
Normal file
40
client/src/pages/servers/servers.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const Servers = () => {
|
||||
return (
|
||||
<div id="servers" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Add Server</h2>
|
||||
<form id="serverForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="serverName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Hostname:</label>
|
||||
<input type="text" id="serverHostname" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>gRPC Port:</label>
|
||||
<input type="number" id="serverPort" value="2053" />
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add Server
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Servers List</h2>
|
||||
<div id="serversList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ServersRoute: RouteObject = {
|
||||
path: '/servers',
|
||||
Component: Servers,
|
||||
};
|
1
client/src/pages/users/index.ts
Normal file
1
client/src/pages/users/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './users';
|
40
client/src/pages/users/users.tsx
Normal file
40
client/src/pages/users/users.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { RouteObject } from 'react-router';
|
||||
|
||||
export const Users = () => {
|
||||
return (
|
||||
<div id="users" className="tab-content active">
|
||||
<div className="section">
|
||||
<h2>Add User</h2>
|
||||
<form id="userForm">
|
||||
<div className="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" id="userName" required />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Comment:</label>
|
||||
<input type="text" id="userComment" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Telegram ID:</label>
|
||||
<input type="number" id="userTelegram" />
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add User
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="section">
|
||||
<h2>Users List</h2>
|
||||
<div id="usersList" className="loading">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const UsersRoute: RouteObject = {
|
||||
path: '/users',
|
||||
Component: Users,
|
||||
};
|
@@ -1,9 +1,25 @@
|
||||
import { createBrowserRouter } from 'react-router';
|
||||
import { Home } from '../pages';
|
||||
import {
|
||||
Home,
|
||||
DashboardRoute,
|
||||
ServersRoute,
|
||||
InboundTemplatesRoute,
|
||||
CertificatesRoute,
|
||||
InboundBindingRoute,
|
||||
UsersRoute,
|
||||
} from '../pages';
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
Component: Home,
|
||||
children: [
|
||||
DashboardRoute,
|
||||
ServersRoute,
|
||||
InboundTemplatesRoute,
|
||||
CertificatesRoute,
|
||||
InboundBindingRoute,
|
||||
UsersRoute,
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
Reference in New Issue
Block a user