[management, reverse proxy] Add reverse proxy feature (#5291)

* implement reverse proxy


---------

Co-authored-by: Alisdair MacLeod <git@alisdairmacleod.co.uk>
Co-authored-by: mlsmaycon <mlsmaycon@gmail.com>
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
Co-authored-by: Viktor Liu <viktor@netbird.io>
Co-authored-by: Diego Noguês <diego.sure@gmail.com>
Co-authored-by: Diego Noguês <49420+diegocn@users.noreply.github.com>
Co-authored-by: Bethuel Mmbaga <bethuelmbaga12@gmail.com>
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
Co-authored-by: Ashley Mensah <ashleyamo982@gmail.com>
This commit is contained in:
Pascal Fischer
2026-02-13 19:37:43 +01:00
committed by GitHub
co-authored by Alisdair MacLeod mlsmaycon Eduard Gert Viktor Liu Diego Noguês Diego Noguês Bethuel Mmbaga Zoltan Papp Ashley Mensah
parent edce11b34d
commit f53155562f
225 changed files with 35513 additions and 235 deletions
+137
View File
@@ -0,0 +1,137 @@
import { cn } from "@/utils/helpers";
import { Eye, EyeOff } from "lucide-react";
import * as React from "react";
import { useState } from "react";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
customPrefix?: React.ReactNode;
customSuffix?: React.ReactNode;
maxWidthClass?: string;
icon?: React.ReactNode;
error?: string;
prefixClassName?: string;
showPasswordToggle?: boolean;
variant?: "default" | "darker";
}
const variantStyles = {
default: [
"bg-nb-gray-900 placeholder:text-neutral-400/70 border-nb-gray-700",
"ring-offset-neutral-950/50 focus-visible:ring-neutral-500/20",
],
darker: [
"bg-nb-gray-920 placeholder:text-neutral-400/70 border-nb-gray-800",
"ring-offset-neutral-950/50 focus-visible:ring-neutral-500/20",
],
error: [
"bg-nb-gray-900 placeholder:text-neutral-400/70 border-red-500 text-red-500",
"ring-offset-red-500/10 focus-visible:ring-red-500/10",
],
};
const prefixSuffixStyles = {
default: "bg-nb-gray-900 border-nb-gray-700 text-nb-gray-300",
error: "bg-nb-gray-900 border-red-500 text-nb-gray-300 text-red-500",
};
const Input = React.forwardRef<HTMLInputElement, InputProps>(
(
{
className,
type,
customSuffix,
customPrefix,
icon,
maxWidthClass = "",
error,
variant = "default",
prefixClassName,
showPasswordToggle = false,
...props
},
ref
) => {
const [showPassword, setShowPassword] = useState(false);
const isPasswordType = type === "password";
const inputType = isPasswordType && showPassword ? "text" : type;
const passwordToggle =
isPasswordType && showPasswordToggle ? (
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="hover:text-white transition-all"
aria-label="Toggle password visibility"
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
) : null;
const suffix = passwordToggle || customSuffix;
const activeVariant = error ? "error" : variant;
return (
<>
<div className={cn("flex relative h-[42px]", maxWidthClass)}>
{customPrefix && (
<div
className={cn(
prefixSuffixStyles[error ? "error" : "default"],
"flex h-[42px] w-auto rounded-l-md px-3 py-2 text-sm",
"border items-center whitespace-nowrap",
props.disabled && "opacity-40",
prefixClassName
)}
>
{customPrefix}
</div>
)}
<div
className={cn(
"absolute left-0 top-0 h-full flex items-center text-xs text-nb-gray-300 pl-3 leading-[0]",
props.disabled && "opacity-40"
)}
>
{icon}
</div>
<input
type={inputType}
ref={ref}
{...props}
className={cn(
variantStyles[activeVariant],
"flex h-[42px] w-full rounded-md px-3 py-2 text-sm",
"file:bg-transparent file:text-sm file:font-medium file:border-0",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-40",
"border",
customPrefix && "!border-l-0 !rounded-l-none",
suffix && "!pr-16",
icon && "!pl-10",
className
)}
/>
<div
className={cn(
"absolute right-0 top-0 h-full flex items-center text-xs text-nb-gray-300 pr-4 leading-[0] select-none",
props.disabled && "opacity-30"
)}
>
{suffix}
</div>
</div>
{error && (
<p className="text-xs text-red-500 mt-2">{error}</p>
)}
</>
);
}
);
Input.displayName = "Input";
export { Input };