mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-03 17:26:38 +00:00
Merge branch 'dev' into clients-pops
This commit is contained in:
@@ -65,32 +65,7 @@ import {
|
||||
import Link from "next/link";
|
||||
import { QRCodeCanvas } from "qrcode.react";
|
||||
|
||||
const createSiteFormSchema = z
|
||||
.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(2, { message: "Name must be at least 2 characters." })
|
||||
.max(30, {
|
||||
message: "Name must not be longer than 30 characters."
|
||||
}),
|
||||
method: z.enum(["newt", "wireguard", "local"]),
|
||||
copied: z.boolean(),
|
||||
clientAddress: z.string().optional()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.method !== "local") {
|
||||
return data.copied;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Please confirm that you have copied the config.",
|
||||
path: ["copied"]
|
||||
}
|
||||
);
|
||||
|
||||
type CreateSiteFormValues = z.infer<typeof createSiteFormSchema>;
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type SiteType = "newt" | "wireguard" | "local";
|
||||
|
||||
@@ -125,28 +100,54 @@ export default function Page() {
|
||||
const api = createApiClient({ env });
|
||||
const { orgId } = useParams();
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
|
||||
const createSiteFormSchema = z
|
||||
.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(2, { message: t('nameMin', {len: 2}) })
|
||||
.max(30, {
|
||||
message: t('nameMax', {len: 30})
|
||||
}),
|
||||
method: z.enum(["newt", "wireguard", "local"]),
|
||||
copied: z.boolean(),
|
||||
clientAddress: z.string().optional()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.method !== "local") {
|
||||
return data.copied;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: t('sitesConfirmCopy'),
|
||||
path: ["copied"]
|
||||
}
|
||||
);
|
||||
|
||||
type CreateSiteFormValues = z.infer<typeof createSiteFormSchema>;
|
||||
|
||||
const [tunnelTypes, setTunnelTypes] = useState<
|
||||
ReadonlyArray<TunnelTypeOption>
|
||||
>([
|
||||
{
|
||||
id: "newt",
|
||||
title: "Newt Tunnel (Recommended)",
|
||||
description:
|
||||
"Easiest way to create an entrypoint into your network. No extra setup.",
|
||||
title: t('siteNewtTunnel'),
|
||||
description: t('siteNewtTunnelDescription'),
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: "wireguard",
|
||||
title: "Basic WireGuard",
|
||||
description:
|
||||
"Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
||||
title: t('siteWg'),
|
||||
description: t('siteWgDescription'),
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: "local",
|
||||
title: "Local",
|
||||
description: "Local resources only. No tunneling."
|
||||
title: t('local'),
|
||||
description: t('siteLocalDescription')
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -325,7 +326,7 @@ WantedBy=default.target`
|
||||
};
|
||||
|
||||
const getCommand = () => {
|
||||
const placeholder = ["Unknown command"];
|
||||
const placeholder = [t('unknownCommand')];
|
||||
if (!commands) {
|
||||
return placeholder;
|
||||
}
|
||||
@@ -387,8 +388,8 @@ WantedBy=default.target`
|
||||
if (!siteDefaults || !wgConfig) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error creating site",
|
||||
description: "Key pair or site defaults not found"
|
||||
title: t('siteErrorCreate'),
|
||||
description: t('siteErrorCreateKeyPair')
|
||||
});
|
||||
setCreateLoading(false);
|
||||
return;
|
||||
@@ -405,8 +406,8 @@ WantedBy=default.target`
|
||||
if (!siteDefaults) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error creating site",
|
||||
description: "Site defaults not found"
|
||||
title: t('siteErrorCreate'),
|
||||
description: t('siteErrorCreateDefaults')
|
||||
});
|
||||
setCreateLoading(false);
|
||||
return;
|
||||
@@ -429,7 +430,7 @@ WantedBy=default.target`
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error creating site",
|
||||
title: t('siteErrorCreate'),
|
||||
description: formatAxiosError(e)
|
||||
});
|
||||
});
|
||||
@@ -455,14 +456,14 @@ WantedBy=default.target`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch release info: ${response.statusText}`
|
||||
t('newtErrorFetchReleases', {err: response.statusText})
|
||||
);
|
||||
}
|
||||
const data = await response.json();
|
||||
const latestVersion = data.tag_name;
|
||||
newtVersion = latestVersion;
|
||||
} catch (error) {
|
||||
console.error("Error fetching latest release:", error);
|
||||
console.error(t('newtErrorFetchLatest', {err: error instanceof Error ? error.message : String(error)}));
|
||||
}
|
||||
|
||||
const generatedKeypair = generateKeypair();
|
||||
@@ -529,8 +530,8 @@ WantedBy=default.target`
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<HeaderTitle
|
||||
title="Create Site"
|
||||
description="Follow the steps below to create and connect a new site"
|
||||
title={t('siteCreate')}
|
||||
description={t('siteCreateDescription2')}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -538,7 +539,7 @@ WantedBy=default.target`
|
||||
router.push(`/${orgId}/settings/sites`);
|
||||
}}
|
||||
>
|
||||
See All Sites
|
||||
{t('siteSeeAll')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -548,7 +549,7 @@ WantedBy=default.target`
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Site Information
|
||||
{t('siteInfo')}
|
||||
</SettingsSectionTitle>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -564,7 +565,7 @@ WantedBy=default.target`
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Name
|
||||
{t('name')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -574,8 +575,7 @@ WantedBy=default.target`
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
<FormDescription>
|
||||
This is the display
|
||||
name for the site.
|
||||
{t('siteNameDescription')}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -625,11 +625,10 @@ WantedBy=default.target`
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Tunnel Type
|
||||
{t('tunnelType')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Determine how you want to connect to your
|
||||
site
|
||||
{t('siteTunnelDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -649,18 +648,17 @@ WantedBy=default.target`
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Newt Credentials
|
||||
{t('siteNewtCredentials')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
This is how Newt will authenticate
|
||||
with the server
|
||||
{t('siteNewtCredentialsDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
Newt Endpoint
|
||||
{t('newtEndpoint')}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<CopyToClipboard
|
||||
@@ -672,7 +670,7 @@ WantedBy=default.target`
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
Newt ID
|
||||
{t('newtId')}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<CopyToClipboard
|
||||
@@ -682,7 +680,7 @@ WantedBy=default.target`
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
Newt Secret Key
|
||||
{t('newtSecretKey')}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<CopyToClipboard
|
||||
@@ -695,12 +693,10 @@ WantedBy=default.target`
|
||||
<Alert variant="neutral" className="">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Save Your Credentials
|
||||
{t('siteCredentialsSave')}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
You will only be able to see
|
||||
this once. Make sure to copy it
|
||||
to a secure place.
|
||||
{t('siteCredentialsSaveDescription')}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -735,9 +731,7 @@ WantedBy=default.target`
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
I have
|
||||
copied the
|
||||
config
|
||||
{t('siteConfirmCopy')}
|
||||
</label>
|
||||
</div>
|
||||
<FormMessage />
|
||||
@@ -751,16 +745,16 @@ WantedBy=default.target`
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Install Newt
|
||||
{t('siteInstallNewt')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Get Newt running on your system
|
||||
{t('siteInstallNewtDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<div>
|
||||
<p className="font-bold mb-3">
|
||||
Operating System
|
||||
{t('operatingSystem')}
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||
{platforms.map((os) => (
|
||||
@@ -788,8 +782,8 @@ WantedBy=default.target`
|
||||
{["docker", "podman"].includes(
|
||||
platform
|
||||
)
|
||||
? "Method"
|
||||
: "Architecture"}
|
||||
? t('method')
|
||||
: t('architecture')}
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||
{getArchitectures().map(
|
||||
@@ -816,7 +810,7 @@ WantedBy=default.target`
|
||||
</div>
|
||||
<div className="pt-4">
|
||||
<p className="font-bold mb-3">
|
||||
Commands
|
||||
{t('commands')}
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<CopyTextBox
|
||||
@@ -837,11 +831,10 @@ WantedBy=default.target`
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
WireGuard Configuration
|
||||
{t('WgConfiguration')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Use the following configuration to
|
||||
connect to your network
|
||||
{t('WgConfigurationDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -862,12 +855,10 @@ WantedBy=default.target`
|
||||
<Alert variant="neutral">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Save Your Credentials
|
||||
{t('siteCredentialsSave')}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
You will only be able to see this
|
||||
once. Make sure to copy it to a
|
||||
secure place.
|
||||
{t('siteCredentialsSaveDescription')}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -902,8 +893,7 @@ WantedBy=default.target`
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
I have copied
|
||||
the config
|
||||
{t('siteConfirmCopy')}
|
||||
</label>
|
||||
</div>
|
||||
<FormMessage />
|
||||
@@ -925,7 +915,7 @@ WantedBy=default.target`
|
||||
router.push(`/${orgId}/settings/sites`);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -935,7 +925,7 @@ WantedBy=default.target`
|
||||
form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
>
|
||||
Create Site
|
||||
{t('siteCreate')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user