mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-15 09:26:40 +00:00
all resources at the base domain closes #137
This commit is contained in:
@@ -63,6 +63,8 @@ import { subdomainSchema } from "@server/schemas/subdomainSchema";
|
||||
import Link from "next/link";
|
||||
import { SquareArrowOutUpRight } from "lucide-react";
|
||||
import CopyTextBox from "@app/components/CopyTextBox";
|
||||
import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group";
|
||||
import { Label } from "@app/components/ui/label";
|
||||
|
||||
const createResourceFormSchema = z
|
||||
.object({
|
||||
@@ -71,7 +73,8 @@ const createResourceFormSchema = z
|
||||
siteId: z.number(),
|
||||
http: z.boolean(),
|
||||
protocol: z.string(),
|
||||
proxyPort: z.number().optional()
|
||||
proxyPort: z.number().optional(),
|
||||
isBaseDomain: z.boolean().optional()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
@@ -92,7 +95,7 @@ const createResourceFormSchema = z
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http) {
|
||||
if (data.http && !data.isBaseDomain) {
|
||||
return subdomainSchema.safeParse(data.subdomain).success;
|
||||
}
|
||||
return true;
|
||||
@@ -131,12 +134,15 @@ export default function CreateResourceForm({
|
||||
const [domainSuffix, setDomainSuffix] = useState<string>(org.org.domain);
|
||||
const [showSnippets, setShowSnippets] = useState(false);
|
||||
const [resourceId, setResourceId] = useState<number | null>(null);
|
||||
const [domainType, setDomainType] = useState<"subdomain" | "basedomain">(
|
||||
"subdomain"
|
||||
);
|
||||
|
||||
const form = useForm<CreateResourceFormValues>({
|
||||
resolver: zodResolver(createResourceFormSchema),
|
||||
defaultValues: {
|
||||
subdomain: "",
|
||||
name: "My Resource",
|
||||
name: "",
|
||||
http: true,
|
||||
protocol: "tcp"
|
||||
}
|
||||
@@ -180,7 +186,8 @@ export default function CreateResourceForm({
|
||||
http: data.http,
|
||||
protocol: data.protocol,
|
||||
proxyPort: data.http ? undefined : data.proxyPort,
|
||||
siteId: data.siteId
|
||||
siteId: data.siteId,
|
||||
isBaseDomain: data.isBaseDomain
|
||||
}
|
||||
)
|
||||
.catch((e) => {
|
||||
@@ -246,7 +253,7 @@ export default function CreateResourceForm({
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Your name"
|
||||
placeholder="Resource name"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -291,33 +298,89 @@ export default function CreateResourceForm({
|
||||
/>
|
||||
)}
|
||||
|
||||
{form.watch("http") &&
|
||||
env.flags.allowBaseDomainResources && (
|
||||
<div>
|
||||
<RadioGroup
|
||||
className="flex space-x-4"
|
||||
defaultValue={domainType}
|
||||
onValueChange={(val) => {
|
||||
setDomainType(
|
||||
val as any
|
||||
);
|
||||
form.setValue(
|
||||
"isBaseDomain",
|
||||
val === "basedomain"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="subdomain"
|
||||
id="r1"
|
||||
/>
|
||||
<Label htmlFor="r1">
|
||||
Subdomain
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="basedomain"
|
||||
id="r2"
|
||||
/>
|
||||
<Label htmlFor="r2">
|
||||
Base Domain
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{form.watch("http") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="subdomain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Subdomain
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<CustomDomainInput
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
domainSuffix={
|
||||
domainSuffix
|
||||
}
|
||||
placeholder="Enter subdomain"
|
||||
onChange={(value) =>
|
||||
form.setValue(
|
||||
"subdomain",
|
||||
{!env.flags
|
||||
.allowBaseDomainResources && (
|
||||
<FormLabel>
|
||||
Subdomain
|
||||
</FormLabel>
|
||||
)}
|
||||
{domainType ===
|
||||
"subdomain" ? (
|
||||
<FormControl>
|
||||
<CustomDomainInput
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
domainSuffix={
|
||||
domainSuffix
|
||||
}
|
||||
placeholder="Subdomain"
|
||||
onChange={(
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
) =>
|
||||
form.setValue(
|
||||
"subdomain",
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
) : (
|
||||
<FormControl>
|
||||
<Input
|
||||
value={
|
||||
domainSuffix
|
||||
}
|
||||
readOnly
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<FormDescription>
|
||||
This is the fully
|
||||
qualified domain name
|
||||
@@ -471,9 +534,7 @@ export default function CreateResourceForm({
|
||||
site
|
||||
) => (
|
||||
<CommandItem
|
||||
value={
|
||||
`${site.siteId}:${site.name}:${site.niceId}`
|
||||
}
|
||||
value={`${site.siteId}:${site.name}:${site.niceId}`}
|
||||
key={
|
||||
site.siteId
|
||||
}
|
||||
@@ -567,21 +628,25 @@ export default function CreateResourceForm({
|
||||
)}
|
||||
</CredenzaBody>
|
||||
<CredenzaFooter>
|
||||
{!showSnippets && <Button
|
||||
type="submit"
|
||||
form="create-resource-form"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
Create Resource
|
||||
</Button>}
|
||||
{!showSnippets && (
|
||||
<Button
|
||||
type="submit"
|
||||
form="create-resource-form"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
Create Resource
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showSnippets && <Button
|
||||
loading={loading}
|
||||
onClick={() => goToResource()}
|
||||
>
|
||||
Go to Resource
|
||||
</Button>}
|
||||
{showSnippets && (
|
||||
<Button
|
||||
loading={loading}
|
||||
onClick={() => goToResource()}
|
||||
>
|
||||
Go to Resource
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<CredenzaClose asChild>
|
||||
<Button variant="outline">Close</Button>
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import {
|
||||
InfoIcon,
|
||||
ShieldCheck,
|
||||
ShieldOff
|
||||
} from "lucide-react";
|
||||
import { InfoIcon, ShieldCheck, ShieldOff } from "lucide-react";
|
||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||
import { Separator } from "@app/components/ui/separator";
|
||||
@@ -26,9 +22,12 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
||||
const { org } = useOrgContext();
|
||||
const { resource, authInfo } = useResourceContext();
|
||||
|
||||
const fullUrl = `${resource.ssl ? "https" : "http"}://${
|
||||
resource.subdomain
|
||||
}.${org.org.domain}`;
|
||||
let fullUrl = `${resource.ssl ? "https" : "http"}://`;
|
||||
if (resource.isBaseDomain) {
|
||||
fullUrl = fullUrl + org.org.domain;
|
||||
} else {
|
||||
fullUrl = fullUrl + `${resource.subdomain}.${org.org.domain}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert>
|
||||
@@ -82,7 +81,9 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>Protocol</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<span>{resource.protocol.toUpperCase()}</span>
|
||||
<span>
|
||||
{resource.protocol.toUpperCase()}
|
||||
</span>
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<Separator orientation="vertical" />
|
||||
|
||||
@@ -132,9 +132,8 @@ export default function ReverseProxyTargets(props: {
|
||||
defaultValues: {
|
||||
ip: "",
|
||||
method: resource.http ? "http" : null,
|
||||
port: ""
|
||||
// protocol: "TCP",
|
||||
}
|
||||
} as z.infer<typeof addTargetSchema>
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -51,13 +51,17 @@ import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { subdomainSchema } from "@server/schemas/subdomainSchema";
|
||||
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
|
||||
import { pullEnv } from "@app/lib/pullEnv";
|
||||
import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group";
|
||||
import { Label } from "@app/components/ui/label";
|
||||
|
||||
const GeneralFormSchema = z
|
||||
.object({
|
||||
subdomain: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
proxyPort: z.number().optional(),
|
||||
http: z.boolean()
|
||||
http: z.boolean(),
|
||||
isBaseDomain: z.boolean().optional()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
@@ -78,7 +82,7 @@ const GeneralFormSchema = z
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.http) {
|
||||
if (data.http && !data.isBaseDomain) {
|
||||
return subdomainSchema.safeParse(data.subdomain).success;
|
||||
}
|
||||
return true;
|
||||
@@ -103,9 +107,11 @@ export default function GeneralForm() {
|
||||
const { org } = useOrgContext();
|
||||
const router = useRouter();
|
||||
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const orgId = params.orgId;
|
||||
|
||||
const api = createApiClient(useEnvContext());
|
||||
const api = createApiClient({ env });
|
||||
|
||||
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
@@ -113,13 +119,18 @@ export default function GeneralForm() {
|
||||
const [transferLoading, setTransferLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [domainType, setDomainType] = useState<"subdomain" | "basedomain">(
|
||||
resource.isBaseDomain ? "basedomain" : "subdomain"
|
||||
);
|
||||
|
||||
const form = useForm<GeneralFormValues>({
|
||||
resolver: zodResolver(GeneralFormSchema),
|
||||
defaultValues: {
|
||||
name: resource.name,
|
||||
subdomain: resource.subdomain ? resource.subdomain : undefined,
|
||||
proxyPort: resource.proxyPort ? resource.proxyPort : undefined,
|
||||
http: resource.http
|
||||
http: resource.http,
|
||||
isBaseDomain: resource.isBaseDomain ? true : false
|
||||
},
|
||||
mode: "onChange"
|
||||
});
|
||||
@@ -148,7 +159,8 @@ export default function GeneralForm() {
|
||||
.post(`resource/${resource?.resourceId}`, {
|
||||
name: data.name,
|
||||
subdomain: data.subdomain,
|
||||
proxyPort: data.proxyPort
|
||||
proxyPort: data.proxyPort,
|
||||
isBaseDomain: data.isBaseDomain
|
||||
})
|
||||
.catch((e) => {
|
||||
toast({
|
||||
@@ -170,7 +182,8 @@ export default function GeneralForm() {
|
||||
updateResource({
|
||||
name: data.name,
|
||||
subdomain: data.subdomain,
|
||||
proxyPort: data.proxyPort
|
||||
proxyPort: data.proxyPort,
|
||||
isBaseDomain: data.isBaseDomain
|
||||
});
|
||||
}
|
||||
setSaveLoading(false);
|
||||
@@ -242,40 +255,103 @@ export default function GeneralForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{resource.http ? (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="subdomain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Subdomain</FormLabel>
|
||||
<FormControl>
|
||||
<CustomDomainInput
|
||||
value={
|
||||
field.value || ""
|
||||
}
|
||||
domainSuffix={
|
||||
domainSuffix
|
||||
}
|
||||
placeholder="Enter subdomain"
|
||||
onChange={(value) =>
|
||||
form.setValue(
|
||||
"subdomain",
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
This is the subdomain that
|
||||
will be used to access the
|
||||
resource.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
{resource.http && (
|
||||
<>
|
||||
{env.flags.allowBaseDomainResources && (
|
||||
<div>
|
||||
<RadioGroup
|
||||
className="flex space-x-4"
|
||||
defaultValue={domainType}
|
||||
onValueChange={(val) => {
|
||||
setDomainType(
|
||||
val as any
|
||||
);
|
||||
form.setValue(
|
||||
"isBaseDomain",
|
||||
val === "basedomain"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="subdomain"
|
||||
id="r1"
|
||||
/>
|
||||
<Label htmlFor="r1">
|
||||
Subdomain
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="basedomain"
|
||||
id="r2"
|
||||
/>
|
||||
<Label htmlFor="r2">
|
||||
Base Domain
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="subdomain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
{!env.flags
|
||||
.allowBaseDomainResources && (
|
||||
<FormLabel>
|
||||
Subdomain
|
||||
</FormLabel>
|
||||
)}
|
||||
|
||||
{domainType ===
|
||||
"subdomain" ? (
|
||||
<FormControl>
|
||||
<CustomDomainInput
|
||||
value={
|
||||
field.value ||
|
||||
""
|
||||
}
|
||||
domainSuffix={
|
||||
domainSuffix
|
||||
}
|
||||
placeholder="Enter subdomain"
|
||||
onChange={(
|
||||
value
|
||||
) =>
|
||||
form.setValue(
|
||||
"subdomain",
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
) : (
|
||||
<FormControl>
|
||||
<Input
|
||||
value={
|
||||
domainSuffix
|
||||
}
|
||||
readOnly
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<FormDescription>
|
||||
This is the subdomain
|
||||
that will be used to
|
||||
access the resource.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!resource.http && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="proxyPort"
|
||||
|
||||
Reference in New Issue
Block a user