mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-12 05:36:38 +00:00
add snippets to create resource
This commit is contained in:
@@ -137,7 +137,8 @@ LQIDAQAB
|
|||||||
hostId: this.hostId,
|
hostId: this.hostId,
|
||||||
isHostLicensed: true,
|
isHostLicensed: true,
|
||||||
isLicenseValid: false,
|
isLicenseValid: false,
|
||||||
maxSites: undefined
|
maxSites: undefined,
|
||||||
|
usedSites: siteCount.value
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ import { SwitchInput } from "@app/components/SwitchInput";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { isTargetValid } from "@server/lib/validators";
|
import { isTargetValid } from "@server/lib/validators";
|
||||||
import { tlsNameSchema } from "@server/lib/schemas";
|
import { tlsNameSchema } from "@server/lib/schemas";
|
||||||
|
import { ChevronsUpDown } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger
|
||||||
|
} from "@app/components/ui/collapsible";
|
||||||
|
|
||||||
const addTargetSchema = z.object({
|
const addTargetSchema = z.object({
|
||||||
ip: z.string().refine(isTargetValid),
|
ip: z.string().refine(isTargetValid),
|
||||||
@@ -145,6 +151,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
const [proxySettingsLoading, setProxySettingsLoading] = useState(false);
|
const [proxySettingsLoading, setProxySettingsLoading] = useState(false);
|
||||||
|
|
||||||
const [pageLoading, setPageLoading] = useState(true);
|
const [pageLoading, setPageLoading] = useState(true);
|
||||||
|
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const addTargetForm = useForm({
|
const addTargetForm = useForm({
|
||||||
@@ -589,26 +596,57 @@ export default function ReverseProxyTargets(props: {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Collapsible
|
||||||
|
open={isAdvancedOpen}
|
||||||
|
onOpenChange={setIsAdvancedOpen}
|
||||||
|
className="space-y-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between space-x-4">
|
||||||
|
<CollapsibleTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
size="sm"
|
||||||
|
className="p-0 flex items-center justify-start gap-2 w-full"
|
||||||
|
>
|
||||||
|
<h4 className="text-sm font-semibold">
|
||||||
|
Advanced TLS Settings
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<ChevronsUpDown className="h-4 w-4" />
|
||||||
|
<span className="sr-only">
|
||||||
|
Toggle
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
</div>
|
||||||
|
<CollapsibleContent className="space-y-2">
|
||||||
<FormField
|
<FormField
|
||||||
control={tlsSettingsForm.control}
|
control={
|
||||||
|
tlsSettingsForm.control
|
||||||
|
}
|
||||||
name="tlsServerName"
|
name="tlsServerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
TLS Server Name (SNI)
|
TLS Server Name
|
||||||
|
(SNI)
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
The TLS Server Name to use
|
The TLS Server Name
|
||||||
for SNI. Leave empty to use
|
to use for SNI.
|
||||||
|
Leave empty to use
|
||||||
the default.
|
the default.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
</SettingsSectionForm>
|
</SettingsSectionForm>
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ import {
|
|||||||
} from "@app/components/ui/popover";
|
} from "@app/components/ui/popover";
|
||||||
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
|
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
|
||||||
import { cn } from "@app/lib/cn";
|
import { cn } from "@app/lib/cn";
|
||||||
|
import { SquareArrowOutUpRight } from "lucide-react";
|
||||||
|
import CopyTextBox from "@app/components/CopyTextBox";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
const baseResourceFormSchema = z.object({
|
const baseResourceFormSchema = z.object({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
@@ -108,6 +111,8 @@ export default function Page() {
|
|||||||
{ domainId: string; baseDomain: string }[]
|
{ domainId: string; baseDomain: string }[]
|
||||||
>([]);
|
>([]);
|
||||||
const [createLoading, setCreateLoading] = useState(false);
|
const [createLoading, setCreateLoading] = useState(false);
|
||||||
|
const [showSnippets, setShowSnippets] = useState(false);
|
||||||
|
const [resourceId, setResourceId] = useState<number | null>(null);
|
||||||
|
|
||||||
const resourceTypes: ReadonlyArray<ResourceTypeOption> = [
|
const resourceTypes: ReadonlyArray<ResourceTypeOption> = [
|
||||||
{
|
{
|
||||||
@@ -202,7 +207,14 @@ export default function Page() {
|
|||||||
|
|
||||||
if (res && res.status === 201) {
|
if (res && res.status === 201) {
|
||||||
const id = res.data.data.resourceId;
|
const id = res.data.data.resourceId;
|
||||||
|
setResourceId(id);
|
||||||
|
|
||||||
|
if (isHttp) {
|
||||||
router.push(`/${orgId}/settings/resources/${id}`);
|
router.push(`/${orgId}/settings/resources/${id}`);
|
||||||
|
} else {
|
||||||
|
setShowSnippets(true);
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error creating resource:", e);
|
console.error("Error creating resource:", e);
|
||||||
@@ -301,6 +313,7 @@ export default function Page() {
|
|||||||
|
|
||||||
{!loadingPage && (
|
{!loadingPage && (
|
||||||
<div>
|
<div>
|
||||||
|
{!showSnippets ? (
|
||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
@@ -324,13 +337,15 @@ export default function Page() {
|
|||||||
Name
|
Name
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
This is the display
|
This is the
|
||||||
name for the
|
display name for
|
||||||
resource.
|
the resource.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -421,8 +436,9 @@ export default function Page() {
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
This site will
|
This site will
|
||||||
provide connectivity
|
provide
|
||||||
to the resource.
|
connectivity to
|
||||||
|
the resource.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -483,10 +499,13 @@ export default function Page() {
|
|||||||
httpForm.control
|
httpForm.control
|
||||||
}
|
}
|
||||||
name="isBaseDomain"
|
name="isBaseDomain"
|
||||||
render={({ field }) => (
|
render={({
|
||||||
|
field
|
||||||
|
}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Domain Type
|
Domain
|
||||||
|
Type
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
value={
|
value={
|
||||||
@@ -608,9 +627,10 @@ export default function Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
The subdomain where
|
The subdomain
|
||||||
your resource will
|
where your
|
||||||
be accessible.
|
resource will be
|
||||||
|
accessible.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -623,10 +643,13 @@ export default function Page() {
|
|||||||
httpForm.control
|
httpForm.control
|
||||||
}
|
}
|
||||||
name="domainId"
|
name="domainId"
|
||||||
render={({ field }) => (
|
render={({
|
||||||
|
field
|
||||||
|
}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Base Domain
|
Base
|
||||||
|
Domain
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={
|
onValueChange={
|
||||||
@@ -692,7 +715,9 @@ export default function Page() {
|
|||||||
id="tcp-udp-settings-form"
|
id="tcp-udp-settings-form"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
control={tcpUdpForm.control}
|
control={
|
||||||
|
tcpUdpForm.control
|
||||||
|
}
|
||||||
name="protocol"
|
name="protocol"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
@@ -725,7 +750,9 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={tcpUdpForm.control}
|
control={
|
||||||
|
tcpUdpForm.control
|
||||||
|
}
|
||||||
name="proxyPort"
|
name="proxyPort"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
@@ -759,8 +786,9 @@ export default function Page() {
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
The external
|
The external
|
||||||
port number to
|
port number
|
||||||
proxy requests.
|
to proxy
|
||||||
|
requests.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -771,7 +799,6 @@ export default function Page() {
|
|||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
</SettingsContainer>
|
|
||||||
|
|
||||||
<div className="flex justify-end space-x-2 mt-8">
|
<div className="flex justify-end space-x-2 mt-8">
|
||||||
<Button
|
<Button
|
||||||
@@ -801,6 +828,81 @@ export default function Page() {
|
|||||||
Create Resource
|
Create Resource
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</SettingsContainer>
|
||||||
|
) : (
|
||||||
|
<SettingsContainer>
|
||||||
|
<SettingsSection>
|
||||||
|
<SettingsSectionHeader>
|
||||||
|
<SettingsSectionTitle>
|
||||||
|
Configuration Snippets
|
||||||
|
</SettingsSectionTitle>
|
||||||
|
<SettingsSectionDescription>
|
||||||
|
Copy and paste these configuration snippets to set up your TCP/UDP resource
|
||||||
|
</SettingsSectionDescription>
|
||||||
|
</SettingsSectionHeader>
|
||||||
|
<SettingsSectionBody>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-lg font-semibold">
|
||||||
|
Traefik: Add Entrypoints
|
||||||
|
</h3>
|
||||||
|
<CopyTextBox
|
||||||
|
text={`entryPoints:
|
||||||
|
${tcpUdpForm.getValues("protocol")}-${tcpUdpForm.getValues("proxyPort")}:
|
||||||
|
address: ":${tcpUdpForm.getValues("proxyPort")}/${tcpUdpForm.getValues("protocol")}"`}
|
||||||
|
wrapText={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-lg font-semibold">
|
||||||
|
Gerbil: Expose Ports in Docker Compose
|
||||||
|
</h3>
|
||||||
|
<CopyTextBox
|
||||||
|
text={`ports:
|
||||||
|
- ${tcpUdpForm.getValues("proxyPort")}:${tcpUdpForm.getValues("proxyPort")}${tcpUdpForm.getValues("protocol") === "tcp" ? "" : "/" + tcpUdpForm.getValues("protocol")}`}
|
||||||
|
wrapText={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className="text-sm text-primary flex items-center gap-1"
|
||||||
|
href="https://docs.fossorial.io/Pangolin/tcp-udp"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Learn how to configure TCP/UDP resources
|
||||||
|
</span>
|
||||||
|
<SquareArrowOutUpRight size={14} />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</SettingsSectionBody>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
|
<div className="flex justify-end space-x-2 mt-8">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/${orgId}/settings/resources`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Back to Resources
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/${orgId}/settings/resources/${resourceId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Go to Resource
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</SettingsContainer>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -57,9 +57,7 @@ import {
|
|||||||
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
||||||
import { Checkbox } from "@app/components/ui/checkbox";
|
import { Checkbox } from "@app/components/ui/checkbox";
|
||||||
import { GenerateAccessTokenResponse } from "@server/routers/accessToken";
|
import { GenerateAccessTokenResponse } from "@server/routers/accessToken";
|
||||||
import {
|
import { constructShareLink } from "@app/lib/shareLinks";
|
||||||
constructShareLink
|
|
||||||
} from "@app/lib/shareLinks";
|
|
||||||
import { ShareLinkRow } from "./ShareLinksTable";
|
import { ShareLinkRow } from "./ShareLinksTable";
|
||||||
import { QRCodeCanvas, QRCodeSVG } from "qrcode.react";
|
import { QRCodeCanvas, QRCodeSVG } from "qrcode.react";
|
||||||
import {
|
import {
|
||||||
@@ -528,11 +526,9 @@ export default function CreateShareLinkForm({
|
|||||||
accessTokenId
|
accessTokenId
|
||||||
}
|
}
|
||||||
token={accessToken}
|
token={accessToken}
|
||||||
resourceUrl={
|
resourceUrl={form.getValues(
|
||||||
form.getValues(
|
|
||||||
"resourceUrl"
|
"resourceUrl"
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ export default function LicenseViolation() {
|
|||||||
return (
|
return (
|
||||||
<div className="fixed bottom-0 left-0 right-0 w-full bg-yellow-500 text-black p-4 text-center z-50">
|
<div className="fixed bottom-0 left-0 right-0 w-full bg-yellow-500 text-black p-4 text-center z-50">
|
||||||
<p>
|
<p>
|
||||||
License Violation: Using {licenseStatus.usedSites} sites
|
License Violation: This server is using{" "}
|
||||||
exceeds your licensed limit of {licenseStatus.maxSites}{" "}
|
{licenseStatus.usedSites} sites which exceeds its licensed
|
||||||
sites. Follow license terms to continue using all features.
|
limit of {licenseStatus.maxSites} sites. Follow license
|
||||||
|
terms to continue using all features.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user