mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-15 17:36:37 +00:00
Merge branch 'dev' into feat/login-page-customization
This commit is contained in:
@@ -26,9 +26,6 @@ import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { GetSessionTransferTokenRenponse } from "@server/routers/auth/types";
|
||||
import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken";
|
||||
import { GetOrgTierResponse } from "@server/routers/billing/types";
|
||||
import { TierId } from "@server/lib/billing/tiers";
|
||||
import { getCachedSubscription } from "@app/lib/api/getCachedSubscription";
|
||||
import { replacePlaceholder } from "@app/lib/replacePlaceholder";
|
||||
import { isOrgSubscribed } from "@app/lib/api/isOrgSubscribed";
|
||||
|
||||
|
||||
@@ -45,7 +45,9 @@ export default function Setup2FAPage() {
|
||||
<CardHeader>
|
||||
<CardTitle>{t("otpSetup")}</CardTitle>
|
||||
<CardDescription>
|
||||
{t("adminEnabled2FaOnYourAccount", { email: email || "your account" })}
|
||||
{t("adminEnabled2FaOnYourAccount", {
|
||||
email: email || "your account"
|
||||
})}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
@@ -14,8 +14,11 @@ export const dynamic = "force-dynamic";
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ orgId: string; idpId: string }>;
|
||||
searchParams: Promise<{
|
||||
code: string;
|
||||
state: string;
|
||||
code?: string;
|
||||
state?: string;
|
||||
error?: string;
|
||||
error_description?: string;
|
||||
error_uri?: string;
|
||||
}>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
@@ -25,15 +28,17 @@ export default async function Page(props: {
|
||||
const allCookies = await cookies();
|
||||
const stateCookie = allCookies.get("p_oidc_state")?.value;
|
||||
|
||||
|
||||
const idpRes = await cache(
|
||||
async () => await priv.get<AxiosResponse<GetIdpResponse>>(`/idp/${params.idpId}`)
|
||||
async () =>
|
||||
await priv.get<AxiosResponse<GetIdpResponse>>(
|
||||
`/idp/${params.idpId}`
|
||||
)
|
||||
)();
|
||||
|
||||
const foundIdp = idpRes.data?.data?.idp;
|
||||
|
||||
if (!foundIdp) {
|
||||
return <div>{t('idpErrorNotFound')}</div>;
|
||||
return <div>{t("idpErrorNotFound")}</div>;
|
||||
}
|
||||
|
||||
const allHeaders = await headers();
|
||||
@@ -59,6 +64,14 @@ export default async function Page(props: {
|
||||
}
|
||||
}
|
||||
|
||||
const providerError = searchParams.error
|
||||
? {
|
||||
error: searchParams.error,
|
||||
description: searchParams.error_description,
|
||||
uri: searchParams.error_uri
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ValidateOidcToken
|
||||
@@ -69,6 +82,7 @@ export default async function Page(props: {
|
||||
expectedState={searchParams.state}
|
||||
stateCookie={stateCookie}
|
||||
idp={{ name: foundIdp.name }}
|
||||
providerError={providerError}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ export default async function AuthLayout({ children }: AuthLayoutProps) {
|
||||
const t = await getTranslations();
|
||||
let hideFooter = false;
|
||||
|
||||
let licenseStatus: GetLicenseStatusResponse | null = null;
|
||||
if (build == "enterprise") {
|
||||
const licenseStatusRes = await cache(
|
||||
async () =>
|
||||
@@ -30,10 +31,12 @@ export default async function AuthLayout({ children }: AuthLayoutProps) {
|
||||
"/license/status"
|
||||
)
|
||||
)();
|
||||
licenseStatus = licenseStatusRes.data.data;
|
||||
if (
|
||||
env.branding.hideAuthLayoutFooter &&
|
||||
licenseStatusRes.data.data.isHostLicensed &&
|
||||
licenseStatusRes.data.data.isLicenseValid
|
||||
licenseStatusRes.data.data.isLicenseValid &&
|
||||
licenseStatusRes.data.data.tier !== "personal"
|
||||
) {
|
||||
hideFooter = true;
|
||||
}
|
||||
@@ -83,6 +86,23 @@ export default async function AuthLayout({ children }: AuthLayoutProps) {
|
||||
? t("enterpriseEdition")
|
||||
: t("pangolinCloud")}
|
||||
</span>
|
||||
{build === "enterprise" &&
|
||||
licenseStatus?.isHostLicensed &&
|
||||
licenseStatus?.isLicenseValid &&
|
||||
licenseStatus?.tier === "personal" ? (
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
<span>{t("personalUseOnly")}</span>
|
||||
</>
|
||||
) : null}
|
||||
{build === "enterprise" &&
|
||||
(!licenseStatus?.isHostLicensed ||
|
||||
!licenseStatus?.isLicenseValid) ? (
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
<span>{t("unlicensed")}</span>
|
||||
</>
|
||||
) : null}
|
||||
{build === "saas" && (
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
|
||||
@@ -19,7 +19,9 @@ export default async function DeviceLoginPage({ searchParams }: Props) {
|
||||
const redirectDestination = code
|
||||
? `/auth/login/device?code=${encodeURIComponent(code)}`
|
||||
: "/auth/login/device";
|
||||
redirect(`/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectDestination)}`);
|
||||
redirect(
|
||||
`/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectDestination)}`
|
||||
);
|
||||
}
|
||||
|
||||
const userName = user?.name || user?.username || "";
|
||||
|
||||
@@ -26,7 +26,9 @@ export default function DeviceAuthSuccessPage() {
|
||||
<BrandingLogo height={logoHeight} width={logoWidth} />
|
||||
</div>
|
||||
<div className="text-center space-y-1 pt-3">
|
||||
<p className="text-muted-foreground">{t("deviceActivation")}</p>
|
||||
<p className="text-muted-foreground">
|
||||
{t("deviceActivation")}
|
||||
</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-6">
|
||||
|
||||
@@ -98,7 +98,11 @@ export default async function Page(props: {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DashboardLoginForm redirect={redirectUrl} idps={loginIdps} forceLogin={forceLogin} />
|
||||
<DashboardLoginForm
|
||||
redirect={redirectUrl}
|
||||
idps={loginIdps}
|
||||
forceLogin={forceLogin}
|
||||
/>
|
||||
|
||||
{(!signUpDisabled || isInvite) && (
|
||||
<p className="text-center text-muted-foreground mt-4">
|
||||
|
||||
@@ -88,18 +88,18 @@ export default function ResetPasswordForm({
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
email: z.email({ message: t('emailInvalid') }),
|
||||
token: z.string().min(8, { message: t('tokenInvalid') }),
|
||||
email: z.email({ message: t("emailInvalid") }),
|
||||
token: z.string().min(8, { message: t("tokenInvalid") }),
|
||||
password: passwordSchema,
|
||||
confirmPassword: passwordSchema
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
path: ["confirmPassword"],
|
||||
message: t('passwordNotMatch')
|
||||
message: t("passwordNotMatch")
|
||||
});
|
||||
|
||||
const mfaSchema = z.object({
|
||||
code: z.string().length(6, { message: t('pincodeInvalid') })
|
||||
code: z.string().length(6, { message: t("pincodeInvalid") })
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
@@ -139,8 +139,8 @@ export default function ResetPasswordForm({
|
||||
} as RequestPasswordResetBody
|
||||
)
|
||||
.catch((e) => {
|
||||
setError(formatAxiosError(e, t('errorOccurred')));
|
||||
console.error(t('passwordErrorRequestReset'), e);
|
||||
setError(formatAxiosError(e, t("errorOccurred")));
|
||||
console.error(t("passwordErrorRequestReset"), e);
|
||||
setIsSubmitting(false);
|
||||
});
|
||||
|
||||
@@ -169,8 +169,8 @@ export default function ResetPasswordForm({
|
||||
} as ResetPasswordBody
|
||||
)
|
||||
.catch((e) => {
|
||||
setError(formatAxiosError(e, t('errorOccurred')));
|
||||
console.error(t('passwordErrorReset'), e);
|
||||
setError(formatAxiosError(e, t("errorOccurred")));
|
||||
console.error(t("passwordErrorReset"), e);
|
||||
setIsSubmitting(false);
|
||||
});
|
||||
|
||||
@@ -186,7 +186,11 @@ export default function ResetPasswordForm({
|
||||
return;
|
||||
}
|
||||
|
||||
setSuccessMessage(quickstart ? t('accountSetupSuccess') : t('passwordResetSuccess'));
|
||||
setSuccessMessage(
|
||||
quickstart
|
||||
? t("accountSetupSuccess")
|
||||
: t("passwordResetSuccess")
|
||||
);
|
||||
|
||||
// Auto-login after successful password reset
|
||||
try {
|
||||
@@ -208,7 +212,10 @@ export default function ResetPasswordForm({
|
||||
try {
|
||||
await api.post("/auth/verify-email/request");
|
||||
} catch (verificationError) {
|
||||
console.error("Failed to send verification code:", verificationError);
|
||||
console.error(
|
||||
"Failed to send verification code:",
|
||||
verificationError
|
||||
);
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
@@ -229,7 +236,6 @@ export default function ResetPasswordForm({
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
}, 1500);
|
||||
|
||||
} catch (loginError) {
|
||||
// Auto-login failed, but password reset was successful
|
||||
console.error("Auto-login failed:", loginError);
|
||||
@@ -251,13 +257,14 @@ export default function ResetPasswordForm({
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{quickstart ? t('completeAccountSetup') : t('passwordReset')}
|
||||
{quickstart
|
||||
? t("completeAccountSetup")
|
||||
: t("passwordReset")}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{quickstart
|
||||
? t('completeAccountSetupDescription')
|
||||
: t('passwordResetDescription')
|
||||
}
|
||||
? t("completeAccountSetupDescription")
|
||||
: t("passwordResetDescription")}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -276,16 +283,19 @@ export default function ResetPasswordForm({
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('email')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("email")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
<FormDescription>
|
||||
{quickstart
|
||||
? t('accountSetupSent')
|
||||
: t('passwordResetSent')
|
||||
}
|
||||
? t("accountSetupSent")
|
||||
: t(
|
||||
"passwordResetSent"
|
||||
)}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -306,7 +316,9 @@ export default function ResetPasswordForm({
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('email')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("email")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
@@ -326,9 +338,12 @@ export default function ResetPasswordForm({
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{quickstart
|
||||
? t('accountSetupCode')
|
||||
: t('passwordResetCode')
|
||||
}
|
||||
? t(
|
||||
"accountSetupCode"
|
||||
)
|
||||
: t(
|
||||
"passwordResetCode"
|
||||
)}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -339,9 +354,12 @@ export default function ResetPasswordForm({
|
||||
<FormMessage />
|
||||
<FormDescription>
|
||||
{quickstart
|
||||
? t('accountSetupCodeDescription')
|
||||
: t('passwordResetCodeDescription')
|
||||
}
|
||||
? t(
|
||||
"accountSetupCodeDescription"
|
||||
)
|
||||
: t(
|
||||
"passwordResetCodeDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -355,9 +373,8 @@ export default function ResetPasswordForm({
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{quickstart
|
||||
? t('passwordCreate')
|
||||
: t('passwordNew')
|
||||
}
|
||||
? t("passwordCreate")
|
||||
: t("passwordNew")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -376,9 +393,12 @@ export default function ResetPasswordForm({
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{quickstart
|
||||
? t('passwordCreateConfirm')
|
||||
: t('passwordNewConfirm')
|
||||
}
|
||||
? t(
|
||||
"passwordCreateConfirm"
|
||||
)
|
||||
: t(
|
||||
"passwordNewConfirm"
|
||||
)}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -407,7 +427,7 @@ export default function ResetPasswordForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t('pincodeAuth')}
|
||||
{t("pincodeAuth")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex justify-center">
|
||||
@@ -475,8 +495,10 @@ export default function ResetPasswordForm({
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
{state === "reset"
|
||||
? (quickstart ? t('completeSetup') : t('passwordReset'))
|
||||
: t('pincodeSubmit2')}
|
||||
? quickstart
|
||||
? t("completeSetup")
|
||||
: t("passwordReset")
|
||||
: t("pincodeSubmit2")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -491,9 +513,8 @@ export default function ResetPasswordForm({
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
{quickstart
|
||||
? t('accountSetupSubmit')
|
||||
: t('passwordResetSubmit')
|
||||
}
|
||||
? t("accountSetupSubmit")
|
||||
: t("passwordResetSubmit")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -507,7 +528,7 @@ export default function ResetPasswordForm({
|
||||
mfaForm.reset();
|
||||
}}
|
||||
>
|
||||
{t('passwordBack')}
|
||||
{t("passwordBack")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -521,7 +542,7 @@ export default function ResetPasswordForm({
|
||||
form.reset();
|
||||
}}
|
||||
>
|
||||
{t('backToEmail')}
|
||||
{t("backToEmail")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -35,10 +35,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<>
|
||||
<VerifyEmailForm
|
||||
email={user.email!}
|
||||
redirect={redirectUrl}
|
||||
/>
|
||||
<VerifyEmailForm email={user.email!} redirect={redirectUrl} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user