Merge branch 'dev' into clients-pops

This commit is contained in:
Owen
2025-06-11 11:13:40 -04:00
149 changed files with 13888 additions and 5083 deletions

View File

@@ -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>