mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-24 03:05:43 +02:00
add hybrid splash
This commit is contained in:
parent
8c8a981452
commit
36c0d9aba2
8 changed files with 231 additions and 10 deletions
|
@ -973,6 +973,7 @@
|
|||
"logoutError": "Error logging out",
|
||||
"signingAs": "Signed in as",
|
||||
"serverAdmin": "Server Admin",
|
||||
"managedSelfhosted": "Managed Self-Hosted",
|
||||
"otpEnable": "Enable Two-factor",
|
||||
"otpDisable": "Disable Two-factor",
|
||||
"logout": "Log Out",
|
||||
|
|
|
@ -103,9 +103,7 @@ export class Config {
|
|||
|
||||
private async checkKeyStatus() {
|
||||
const licenseStatus = await license.check();
|
||||
if (
|
||||
!licenseStatus.isHostLicensed
|
||||
) {
|
||||
if (!licenseStatus.isHostLicensed) {
|
||||
this.checkSupporterKey();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ export const configSchema = z
|
|||
}),
|
||||
hybrid: z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
id: z.string().optional(),
|
||||
secret: z.string().optional(),
|
||||
endpoint: z.string().optional(),
|
||||
|
|
|
@ -15,6 +15,7 @@ import * as accessToken from "./accessToken";
|
|||
import * as idp from "./idp";
|
||||
import * as license from "./license";
|
||||
import * as apiKeys from "./apiKeys";
|
||||
import * as hybrid from "./hybrid";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import {
|
||||
verifyAccessTokenAccess,
|
||||
|
@ -951,7 +952,8 @@ authRouter.post(
|
|||
rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 15,
|
||||
keyGenerator: (req) => `requestEmailVerificationCode:${req.body.email || req.ip}`,
|
||||
keyGenerator: (req) =>
|
||||
`requestEmailVerificationCode:${req.body.email || req.ip}`,
|
||||
handler: (req, res, next) => {
|
||||
const message = `You can only request an email verification code ${15} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
|
@ -972,7 +974,8 @@ authRouter.post(
|
|||
rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 15,
|
||||
keyGenerator: (req) => `requestPasswordReset:${req.body.email || req.ip}`,
|
||||
keyGenerator: (req) =>
|
||||
`requestPasswordReset:${req.body.email || req.ip}`,
|
||||
handler: (req, res, next) => {
|
||||
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
|
@ -1066,7 +1069,8 @@ authRouter.post(
|
|||
rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // Allow 5 security key registrations per 15 minutes
|
||||
keyGenerator: (req) => `securityKeyRegister:${req.user?.userId || req.ip}`,
|
||||
keyGenerator: (req) =>
|
||||
`securityKeyRegister:${req.user?.userId || req.ip}`,
|
||||
handler: (req, res, next) => {
|
||||
const message = `You can only register a security key ${5} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
|
|
176
src/app/admin/managed/page.tsx
Normal file
176
src/app/admin/managed/page.tsx
Normal file
|
@ -0,0 +1,176 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
SettingsContainer,
|
||||
SettingsSection,
|
||||
SettingsSectionTitle as SectionTitle,
|
||||
SettingsSectionBody,
|
||||
SettingsSectionFooter
|
||||
} from "@app/components/Settings";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import { Alert } from "@app/components/ui/alert";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
Shield,
|
||||
Zap,
|
||||
RefreshCw,
|
||||
Activity,
|
||||
Wrench,
|
||||
CheckCircle,
|
||||
ExternalLink
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function ManagedPage() {
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
title="Managed Self-Hosted"
|
||||
description="More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles"
|
||||
/>
|
||||
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionBody>
|
||||
<p className="text-muted-foreground mb-4">
|
||||
<strong>Managed Self-Hosted Pangolin</strong> is a
|
||||
deployment option designed for people who want
|
||||
simplicity and extra reliability while still keeping
|
||||
their data private and self-hosted.
|
||||
</p>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
With this option, you still run your own Pangolin
|
||||
node — your tunnels, SSL termination, and traffic
|
||||
all stay on your server. The difference is that
|
||||
management and monitoring are handled through our
|
||||
cloud dashboard, which unlocks a number of benefits:
|
||||
</p>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 py-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
Simpler operations
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No need to run your own mail server
|
||||
or set up complex alerting. You'll
|
||||
get health checks and downtime
|
||||
alerts out of the box.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<RefreshCw className="w-5 h-5 text-blue-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
Automatic updates
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The cloud dashboard evolves quickly,
|
||||
so you get new features and bug
|
||||
fixes without having to manually
|
||||
pull new containers every time.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Wrench className="w-5 h-5 text-orange-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
Less maintenance
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No database migrations, backups, or
|
||||
extra infrastructure to manage. We
|
||||
handle that in the cloud.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<Activity className="w-5 h-5 text-purple-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
Cloud failover
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
If your node goes down, your tunnels
|
||||
can temporarily fail over to our
|
||||
cloud points of presence until you
|
||||
bring it back online.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Shield className="w-5 h-5 text-indigo-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
High availability (PoPs)
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You can also attach multiple nodes
|
||||
to your account for redundancy and
|
||||
better performance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Zap className="w-5 h-5 text-yellow-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-medium">
|
||||
Future enhancements
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
We're planning to add more
|
||||
analytics, alerting, and management
|
||||
tools to make your deployment even
|
||||
more robust.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Alert
|
||||
variant="neutral"
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
Read the docs to learn more about the Managed
|
||||
Self-Hosted option in our{" "}
|
||||
<Link
|
||||
href="https://docs.digpangolin.com/self-host/advanced/convert-to-managed"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline text-primary flex items-center gap-1"
|
||||
>
|
||||
documentation
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</Link>
|
||||
.
|
||||
</Alert>
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Link
|
||||
href="https://docs.digpangolin.com/self-host/advanced/convert-to-managed"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline text-primary flex items-center gap-1"
|
||||
>
|
||||
<Button>
|
||||
Convert This Node to Managed Self-Hosted
|
||||
</Button>
|
||||
</Link>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
</SettingsContainer>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -13,10 +13,12 @@ import {
|
|||
TicketCheck,
|
||||
User,
|
||||
Globe, // Added from 'dev' branch
|
||||
MonitorUp // Added from 'dev' branch
|
||||
MonitorUp, // Added from 'dev' branch
|
||||
Zap
|
||||
} from "lucide-react";
|
||||
|
||||
export type SidebarNavSection = { // Added from 'dev' branch
|
||||
export type SidebarNavSection = {
|
||||
// Added from 'dev' branch
|
||||
heading: string;
|
||||
items: SidebarNavItem[];
|
||||
};
|
||||
|
@ -108,6 +110,15 @@ export const adminNavSections: SidebarNavSection[] = [
|
|||
{
|
||||
heading: "Admin",
|
||||
items: [
|
||||
...(build == "oss"
|
||||
? [
|
||||
{
|
||||
title: "managedSelfhosted",
|
||||
href: "/admin/managed",
|
||||
icon: <Zap className="h-4 w-4" />
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: "sidebarAllUsers",
|
||||
href: "/admin/users",
|
||||
|
|
|
@ -6,7 +6,7 @@ import { OrgSelector } from "@app/components/OrgSelector";
|
|||
import { cn } from "@app/lib/cn";
|
||||
import { ListUserOrgsResponse } from "@server/routers/org";
|
||||
import SupporterStatus from "@app/components/SupporterStatus";
|
||||
import { ExternalLink, Server, BookOpenText } from "lucide-react";
|
||||
import { ExternalLink, Server, BookOpenText, Zap } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useUserContext } from "@app/hooks/useUserContext";
|
||||
|
@ -20,6 +20,7 @@ import {
|
|||
TooltipProvider,
|
||||
TooltipTrigger
|
||||
} from "@app/components/ui/tooltip";
|
||||
import { build } from "@server/build";
|
||||
|
||||
interface LayoutSidebarProps {
|
||||
orgId?: string;
|
||||
|
@ -73,6 +74,35 @@ export function LayoutSidebar({
|
|||
<div className="px-2 pt-1">
|
||||
{!isAdminPage && user.serverAdmin && (
|
||||
<div className="pb-4">
|
||||
{build === "oss" && (
|
||||
<Link
|
||||
href="/admin/managed"
|
||||
className={cn(
|
||||
"flex items-center rounded transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-secondary/50 dark:hover:bg-secondary/20 rounded-md",
|
||||
isSidebarCollapsed
|
||||
? "px-2 py-2 justify-center"
|
||||
: "px-3 py-1.5"
|
||||
)}
|
||||
title={
|
||||
isSidebarCollapsed
|
||||
? t("managedSelfhosted")
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"flex-shrink-0",
|
||||
!isSidebarCollapsed && "mr-2"
|
||||
)}
|
||||
>
|
||||
<Zap className="h-4 w-4" />
|
||||
</span>
|
||||
{!isSidebarCollapsed && (
|
||||
<span>{t("managedSelfhosted")}</span>
|
||||
)}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Link
|
||||
href="/admin"
|
||||
className={cn(
|
||||
|
|
|
@ -25,5 +25,5 @@ export type Env = {
|
|||
disableBasicWireguardSites: boolean;
|
||||
enableClients: boolean;
|
||||
hideSupporterKey: boolean;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue