mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-12 21:30:35 +01:00
fix table filters and update readme
This commit is contained in:
parent
c230e034cf
commit
a35e24bc0e
7 changed files with 65 additions and 48 deletions
25
README.md
25
README.md
|
@ -22,8 +22,13 @@
|
|||
</div>
|
||||
|
||||
<h3 align="center">Tunneled Mesh Reverse Proxy Server with Access Control</h3>
|
||||
<div align="center">
|
||||
|
||||
Pangolin is a self-hosted tunneled reverse proxy management server with identity and access control, designed to securely expose private resources on distributed networks. With Pangolin, you retain full control over your infrastructure while providing a user-friendly and feature-rich solution for managing proxies, authentication, and access, while simplifying complex network setups.
|
||||
_Your own self-hosted zero trust tunnel._
|
||||
|
||||
</div>
|
||||
|
||||
Pangolin is a self-hosted tunneled reverse proxy server with identity and access control, designed to securely expose private resources on distributed networks. Acting as a central hub, it connects isolated networks — even those behind restrictive firewalls — through encrypted tunnels, enabling easy access to remote services without opening ports.
|
||||
|
||||
<img src="public/screenshots/sites.png" alt="Preview"/>
|
||||
|
||||
|
@ -38,12 +43,13 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected
|
|||
- Built-in support for any WireGuard client.
|
||||
- Automated **SSL certificates** (https) via [LetsEncrypt](https://letsencrypt.org/).
|
||||
- Support for HTTP/HTTPS and **raw TCP/UDP services**.
|
||||
- Load balancing.
|
||||
|
||||
### Identity & Access Management
|
||||
|
||||
- Centralized authentication system using platform SSO. **Users will only have to manage one login.**
|
||||
- **Rules based access control for resources.**
|
||||
- Totp with backup codes for two-factor authentication.
|
||||
- **Define access control rules for IPs, IP ranges, and URL paths per resource.**
|
||||
- TOTP with backup codes for two-factor authentication.
|
||||
- Create organizations, each with multiple sites, users, and roles.
|
||||
- **Role-based access control** to manage resource access permissions.
|
||||
- Additional authentication options include:
|
||||
|
@ -61,9 +67,9 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected
|
|||
|
||||
### Easy Deployment
|
||||
|
||||
- Run on any cloud provider or on-premises.
|
||||
- Docker Compose based setup for simplified deployment.
|
||||
- Future-proof installation script for streamlined setup and feature additions.
|
||||
- Run on any VPS.
|
||||
- Use your preferred WireGuard client to connect, or use Newt, our custom user space client for the best experience.
|
||||
|
||||
### Modular Design
|
||||
|
@ -126,17 +132,18 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected
|
|||
|
||||
## Similar Projects and Inspirations
|
||||
|
||||
Pangolin was inspired by several existing projects and concepts:
|
||||
|
||||
- **Cloudflare Tunnels**:
|
||||
**Cloudflare Tunnels**:
|
||||
A similar approach to proxying private resources securely, but Pangolin is a self-hosted alternative, giving you full control over your infrastructure.
|
||||
|
||||
- **Authentik and Authelia**:
|
||||
**Authentik and Authelia**:
|
||||
These projects inspired Pangolin’s centralized authentication system for proxies, enabling robust user and role management.
|
||||
|
||||
## Project Development / Roadmap
|
||||
|
||||
Pangolin is under active development, and we are continuously adding new features and improvements. View the [project board](https://github.com/orgs/fosrl/projects/1) for more detailed info.
|
||||
> [!NOTE]
|
||||
> Pangolin is under heavy development. The roadmap is subject to change as we fix bugs, add new features, and make improvements.
|
||||
|
||||
View the [project board](https://github.com/orgs/fosrl/projects/1) for more detailed info.
|
||||
|
||||
## Licensing
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
@ -9,7 +9,7 @@ import {
|
|||
SortingState,
|
||||
getSortedRowModel,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel,
|
||||
getFilteredRowModel
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Table,
|
||||
|
@ -18,7 +18,7 @@ import {
|
|||
TableContainer,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableRow
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
@ -35,7 +35,7 @@ interface DataTableProps<TData, TValue> {
|
|||
export function RolesDataTable<TData, TValue>({
|
||||
addRole,
|
||||
columns,
|
||||
data,
|
||||
data
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
|
@ -50,13 +50,15 @@ export function RolesDataTable<TData, TValue>({
|
|||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
pageIndex: 0,
|
||||
},
|
||||
pageIndex: 0
|
||||
}
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -102,7 +104,7 @@ export function RolesDataTable<TData, TValue>({
|
|||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext(),
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
|
@ -123,7 +125,7 @@ export function RolesDataTable<TData, TValue>({
|
|||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
SortingState,
|
||||
getSortedRowModel,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel,
|
||||
getFilteredRowModel
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Table,
|
||||
|
@ -18,7 +18,7 @@ import {
|
|||
TableContainer,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableRow
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
@ -35,7 +35,7 @@ interface DataTableProps<TData, TValue> {
|
|||
export function UsersDataTable<TData, TValue>({
|
||||
inviteUser,
|
||||
columns,
|
||||
data,
|
||||
data
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
|
@ -50,13 +50,15 @@ export function UsersDataTable<TData, TValue>({
|
|||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
pageIndex: 0,
|
||||
},
|
||||
pageIndex: 0
|
||||
}
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -102,7 +104,7 @@ export function UsersDataTable<TData, TValue>({
|
|||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext(),
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
|
@ -123,7 +125,7 @@ export function UsersDataTable<TData, TValue>({
|
|||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
SortingState,
|
||||
getSortedRowModel,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel,
|
||||
getFilteredRowModel
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
import {
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
TableContainer,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableRow
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
@ -36,7 +36,7 @@ interface ResourcesDataTableProps<TData, TValue> {
|
|||
export function ResourcesDataTable<TData, TValue>({
|
||||
addResource,
|
||||
columns,
|
||||
data,
|
||||
data
|
||||
}: ResourcesDataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
|
@ -51,13 +51,15 @@ export function ResourcesDataTable<TData, TValue>({
|
|||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
pageIndex: 0,
|
||||
},
|
||||
pageIndex: 0
|
||||
}
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -103,7 +105,7 @@ export function ResourcesDataTable<TData, TValue>({
|
|||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext(),
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
|
@ -124,7 +126,7 @@ export function ResourcesDataTable<TData, TValue>({
|
|||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
|
|
|
@ -51,12 +51,14 @@ export function ShareLinksDataTable<TData, TValue>({
|
|||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
pageIndex: 0
|
||||
}
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
SortingState,
|
||||
getSortedRowModel,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel,
|
||||
getFilteredRowModel
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
import {
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
TableContainer,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableRow
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
@ -36,7 +36,7 @@ interface DataTableProps<TData, TValue> {
|
|||
export function SitesDataTable<TData, TValue>({
|
||||
addSite,
|
||||
columns,
|
||||
data,
|
||||
data
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
|
@ -51,13 +51,15 @@ export function SitesDataTable<TData, TValue>({
|
|||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination: {
|
||||
pageSize: 100,
|
||||
pageIndex: 0,
|
||||
},
|
||||
pageSize: 20,
|
||||
pageIndex: 0
|
||||
}
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -103,7 +105,7 @@ export function SitesDataTable<TData, TValue>({
|
|||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext(),
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
|
@ -124,7 +126,7 @@ export function SitesDataTable<TData, TValue>({
|
|||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
|
|
Loading…
Reference in a new issue