mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-24 12:44:59 +02:00
Merge branch 'dev' of github.com:fosrl/pangolin into dev
This commit is contained in:
commit
84c28645be
6 changed files with 88 additions and 71 deletions
|
@ -1270,5 +1270,7 @@
|
|||
"createDomainSaveTheseRecords": "Save These Records",
|
||||
"createDomainSaveTheseRecordsDescription": "Make sure to save these DNS records as you will not see them again.",
|
||||
"createDomainDnsPropagation": "DNS Propagation",
|
||||
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings."
|
||||
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.",
|
||||
"resourcePortRequired": "Port number is required for non-HTTP resources",
|
||||
"resourcePortNotAllowed": "Port number should not be set for HTTP resources"
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ export default async function migration() {
|
|||
const db = new Database(location);
|
||||
|
||||
try {
|
||||
db.pragma("foreign_keys = OFF");
|
||||
|
||||
db.transaction(() => {
|
||||
db.exec(`
|
||||
CREATE TABLE 'clientSites' (
|
||||
|
@ -99,8 +101,6 @@ export default async function migration() {
|
|||
|
||||
`);
|
||||
|
||||
db.pragma("foreign_keys = OFF");
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE '__new_sites' (
|
||||
'siteId' integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
|
@ -135,8 +135,6 @@ export default async function migration() {
|
|||
ALTER TABLE '__new_sites' RENAME TO 'sites';
|
||||
`);
|
||||
|
||||
db.pragma("foreign_keys = ON");
|
||||
|
||||
db.exec(`
|
||||
ALTER TABLE 'domains' ADD 'type' text;
|
||||
ALTER TABLE 'domains' ADD 'verified' integer DEFAULT 0 NOT NULL;
|
||||
|
@ -148,7 +146,10 @@ export default async function migration() {
|
|||
ALTER TABLE 'user' ADD 'twoFactorSetupRequested' integer DEFAULT 0;
|
||||
ALTER TABLE 'resources' DROP COLUMN 'isBaseDomain';
|
||||
`);
|
||||
})(); // <-- executes the transaction immediately
|
||||
})();
|
||||
|
||||
db.pragma("foreign_keys = ON");
|
||||
|
||||
console.log(`Migrated database schema`);
|
||||
} catch (e) {
|
||||
console.log("Unable to migrate database schema");
|
||||
|
|
|
@ -122,7 +122,20 @@ export default function GeneralForm() {
|
|||
enabled: z.boolean(),
|
||||
subdomain: z.string().optional(),
|
||||
name: z.string().min(1).max(255),
|
||||
domainId: z.string().optional()
|
||||
domainId: z.string().optional(),
|
||||
proxyPort: z.number().int().min(1).max(65535).optional()
|
||||
}).refine((data) => {
|
||||
// For non-HTTP resources, proxyPort should be defined
|
||||
if (!resource.http) {
|
||||
return data.proxyPort !== undefined;
|
||||
}
|
||||
// For HTTP resources, proxyPort should be undefined
|
||||
return data.proxyPort === undefined;
|
||||
}, {
|
||||
message: !resource.http
|
||||
? "Port number is required for non-HTTP resources"
|
||||
: "Port number should not be set for HTTP resources",
|
||||
path: ["proxyPort"]
|
||||
});
|
||||
|
||||
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
||||
|
@ -133,7 +146,8 @@ export default function GeneralForm() {
|
|||
enabled: resource.enabled,
|
||||
name: resource.name,
|
||||
subdomain: resource.subdomain ? resource.subdomain : undefined,
|
||||
domainId: resource.domainId || undefined
|
||||
domainId: resource.domainId || undefined,
|
||||
proxyPort: resource.proxyPort || undefined
|
||||
},
|
||||
mode: "onChange"
|
||||
});
|
||||
|
@ -196,7 +210,8 @@ export default function GeneralForm() {
|
|||
enabled: data.enabled,
|
||||
name: data.name,
|
||||
subdomain: data.subdomain,
|
||||
domainId: data.domainId
|
||||
domainId: data.domainId,
|
||||
proxyPort: data.proxyPort
|
||||
}
|
||||
)
|
||||
.catch((e) => {
|
||||
|
@ -222,7 +237,8 @@ export default function GeneralForm() {
|
|||
enabled: data.enabled,
|
||||
name: data.name,
|
||||
subdomain: data.subdomain,
|
||||
fullDomain: resource.fullDomain
|
||||
fullDomain: resource.fullDomain,
|
||||
proxyPort: data.proxyPort
|
||||
});
|
||||
|
||||
router.refresh();
|
||||
|
@ -333,6 +349,39 @@ export default function GeneralForm() {
|
|||
)}
|
||||
/>
|
||||
|
||||
{!resource.http && (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="proxyPort"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("resourcePortNumber")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value ?? ""}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value
|
||||
? parseInt(e.target.value)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
<FormDescription>
|
||||
{t("resourcePortNumberDescription")}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{resource.http && (
|
||||
<div className="space-y-2">
|
||||
<Label>Domain</Label>
|
||||
|
|
|
@ -25,32 +25,34 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
|||
} else if (type === "wireguard") {
|
||||
return "WireGuard";
|
||||
} else if (type === "local") {
|
||||
return t('local');
|
||||
return t("local");
|
||||
} else {
|
||||
return t('unknown');
|
||||
return t("unknown");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert>
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">{t('siteInfo')}</AlertTitle>
|
||||
<AlertTitle className="font-semibold">{t("siteInfo")}</AlertTitle>
|
||||
<AlertDescription className="mt-4">
|
||||
<InfoSections cols={env.flags.enableClients ? 3 : 2}>
|
||||
{(site.type == "newt" || site.type == "wireguard") && (
|
||||
<>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>{t('status')}</InfoSectionTitle>
|
||||
<InfoSectionTitle>
|
||||
{t("status")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{site.online ? (
|
||||
<div className="text-green-500 flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<span>{t('online')}</span>
|
||||
<span>{t("online")}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-neutral-500 flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full"></div>
|
||||
<span>{t('offline')}</span>
|
||||
<span>{t("offline")}</span>
|
||||
</div>
|
||||
)}
|
||||
</InfoSectionContent>
|
||||
|
@ -58,17 +60,22 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
|||
</>
|
||||
)}
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>{t('connectionType')}</InfoSectionTitle>
|
||||
<InfoSectionTitle>
|
||||
{t("connectionType")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{getConnectionTypeString(site.type)}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>Address</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{site.address?.split("/")[0]}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
|
||||
{env.flags.enableClients && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>Address</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{site.address?.split("/")[0]}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
</InfoSections>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
|
|
@ -894,49 +894,6 @@ WantedBy=default.target`
|
|||
)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-4"
|
||||
id="create-site-form"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="copied"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
defaultChecked={
|
||||
form.getValues(
|
||||
"copied"
|
||||
) as boolean
|
||||
}
|
||||
onCheckedChange={(
|
||||
e
|
||||
) => {
|
||||
form.setValue(
|
||||
"copied",
|
||||
e as boolean
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{t(
|
||||
"siteConfirmCopy"
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
|
|
@ -63,6 +63,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
|
|||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [securityKeyLoading, setSecurityKeyLoading] = useState(false);
|
||||
const hasIdp = idps && idps.length > 0;
|
||||
|
||||
const [mfaRequested, setMfaRequested] = useState(false);
|
||||
|
@ -98,7 +99,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
|
|||
|
||||
async function initiateSecurityKeyAuth() {
|
||||
setShowSecurityKeyPrompt(true);
|
||||
setLoading(true);
|
||||
setSecurityKeyLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
|
@ -117,7 +118,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
|
|||
// Perform WebAuthn authentication
|
||||
try {
|
||||
const credential = await startAuthentication(options);
|
||||
|
||||
|
||||
// Verify authentication
|
||||
const verifyRes = await api.post(
|
||||
"/auth/security-key/authenticate/verify",
|
||||
|
@ -167,7 +168,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
|
|||
}));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setSecurityKeyLoading(false);
|
||||
setShowSecurityKeyPrompt(false);
|
||||
}
|
||||
}
|
||||
|
@ -432,8 +433,8 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
|
|||
variant="outline"
|
||||
className="w-full"
|
||||
onClick={initiateSecurityKeyAuth}
|
||||
loading={loading}
|
||||
disabled={loading || showSecurityKeyPrompt}
|
||||
loading={securityKeyLoading}
|
||||
disabled={securityKeyLoading || showSecurityKeyPrompt}
|
||||
>
|
||||
<FingerprintIcon className="w-4 h-4 mr-2" />
|
||||
{t('securityKeyLogin', {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue