mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-03 09:16:40 +00:00
Merge branch 'dev' into refactor/save-button-positions
This commit is contained in:
56
src/app/[orgId]/settings/general/auth-page/page.tsx
Normal file
56
src/app/[orgId]/settings/general/auth-page/page.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import AuthPageBrandingForm from "@app/components/AuthPageBrandingForm";
|
||||
import AuthPageSettings from "@app/components/private/AuthPageSettings";
|
||||
import { SettingsContainer } from "@app/components/Settings";
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import { getCachedSubscription } from "@app/lib/api/getCachedSubscription";
|
||||
import { build } from "@server/build";
|
||||
import type { GetOrgTierResponse } from "@server/routers/billing/types";
|
||||
import {
|
||||
GetLoginPageBrandingResponse,
|
||||
GetLoginPageResponse
|
||||
} from "@server/routers/loginPage/types";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
export interface AuthPageProps {
|
||||
params: Promise<{ orgId: string }>;
|
||||
}
|
||||
|
||||
export default async function AuthPage(props: AuthPageProps) {
|
||||
const orgId = (await props.params).orgId;
|
||||
let subscriptionStatus: GetOrgTierResponse | null = null;
|
||||
try {
|
||||
const subRes = await getCachedSubscription(orgId);
|
||||
subscriptionStatus = subRes.data.data;
|
||||
} catch {}
|
||||
|
||||
let loginPage: GetLoginPageResponse | null = null;
|
||||
try {
|
||||
if (build === "saas") {
|
||||
const res = await internal.get<AxiosResponse<GetLoginPageResponse>>(
|
||||
`/org/${orgId}/login-page`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
if (res.status === 200) {
|
||||
loginPage = res.data.data;
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
let loginPageBranding: GetLoginPageBrandingResponse | null = null;
|
||||
try {
|
||||
const res = await internal.get<
|
||||
AxiosResponse<GetLoginPageBrandingResponse>
|
||||
>(`/org/${orgId}/login-page-branding`, await authCookieHeader());
|
||||
if (res.status === 200) {
|
||||
loginPageBranding = res.data.data;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
{build === "saas" && <AuthPageSettings loginPage={loginPage} />}
|
||||
<AuthPageBrandingForm orgId={orgId} branding={loginPageBranding} />
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import { HorizontalTabs } from "@app/components/HorizontalTabs";
|
||||
import { HorizontalTabs, type TabItem } from "@app/components/HorizontalTabs";
|
||||
import { verifySession } from "@app/lib/auth/verifySession";
|
||||
import OrgProvider from "@app/providers/OrgProvider";
|
||||
import OrgUserProvider from "@app/providers/OrgUserProvider";
|
||||
import { GetOrgResponse } from "@server/routers/org";
|
||||
import { GetOrgUserResponse } from "@server/routers/user";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
import { cache } from "react";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
||||
import { getCachedOrgUser } from "@app/lib/api/getCachedOrgUser";
|
||||
import { build } from "@server/build";
|
||||
|
||||
type GeneralSettingsProps = {
|
||||
children: React.ReactNode;
|
||||
@@ -23,8 +21,7 @@ export default async function GeneralSettingsPage({
|
||||
}: GeneralSettingsProps) {
|
||||
const { orgId } = await params;
|
||||
|
||||
const getUser = cache(verifySession);
|
||||
const user = await getUser();
|
||||
const user = await verifySession();
|
||||
|
||||
if (!user) {
|
||||
redirect(`/`);
|
||||
@@ -32,13 +29,7 @@ export default async function GeneralSettingsPage({
|
||||
|
||||
let orgUser = null;
|
||||
try {
|
||||
const getOrgUser = cache(async () =>
|
||||
internal.get<AxiosResponse<GetOrgUserResponse>>(
|
||||
`/org/${orgId}/user/${user.userId}`,
|
||||
await authCookieHeader()
|
||||
)
|
||||
);
|
||||
const res = await getOrgUser();
|
||||
const res = await getCachedOrgUser(orgId, user.userId);
|
||||
orgUser = res.data.data;
|
||||
} catch {
|
||||
redirect(`/${orgId}`);
|
||||
@@ -46,13 +37,7 @@ export default async function GeneralSettingsPage({
|
||||
|
||||
let org = null;
|
||||
try {
|
||||
const getOrg = cache(async () =>
|
||||
internal.get<AxiosResponse<GetOrgResponse>>(
|
||||
`/org/${orgId}`,
|
||||
await authCookieHeader()
|
||||
)
|
||||
);
|
||||
const res = await getOrg();
|
||||
const res = await getCachedOrg(orgId);
|
||||
org = res.data.data;
|
||||
} catch {
|
||||
redirect(`/${orgId}`);
|
||||
@@ -60,12 +45,19 @@ export default async function GeneralSettingsPage({
|
||||
|
||||
const t = await getTranslations();
|
||||
|
||||
const navItems = [
|
||||
const navItems: TabItem[] = [
|
||||
{
|
||||
title: t("general"),
|
||||
href: `/{orgId}/settings/general`
|
||||
href: `/{orgId}/settings/general`,
|
||||
exact: true
|
||||
}
|
||||
];
|
||||
if (build !== "oss") {
|
||||
navItems.push({
|
||||
title: t("authPage"),
|
||||
href: `/{orgId}/settings/general/auth-page`
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -43,14 +43,13 @@ import {
|
||||
SettingsSectionTitle,
|
||||
SettingsSectionDescription,
|
||||
SettingsSectionBody,
|
||||
SettingsSectionForm,
|
||||
SettingsSectionFooter
|
||||
SettingsSectionForm
|
||||
} from "@app/components/Settings";
|
||||
import { useUserContext } from "@app/hooks/useUserContext";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { build } from "@server/build";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
import { SecurityFeaturesAlert } from "@app/components/SecurityFeaturesAlert";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
|
||||
import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
@@ -113,29 +112,18 @@ const LOG_RETENTION_OPTIONS = [
|
||||
|
||||
export default function GeneralPage() {
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const { orgUser } = userOrgUserContext();
|
||||
const router = useRouter();
|
||||
const { org } = useOrgContext();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const { user } = useUserContext();
|
||||
const t = useTranslations();
|
||||
const { env } = useEnvContext();
|
||||
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
||||
const subscription = useSubscriptionStatusContext();
|
||||
|
||||
// Check if security features are disabled due to licensing/subscription
|
||||
const isSecurityFeatureDisabled = () => {
|
||||
const isEnterpriseNotLicensed = build === "enterprise" && !isUnlocked();
|
||||
const isSaasNotSubscribed =
|
||||
build === "saas" && !subscription?.isSubscribed();
|
||||
return isEnterpriseNotLicensed || isSaasNotSubscribed;
|
||||
};
|
||||
const { isPaidUser, hasSaasSubscription } = usePaidStatus();
|
||||
|
||||
const [loadingDelete, setLoadingDelete] = useState(false);
|
||||
const [loadingSave, setLoadingSave] = useState(false);
|
||||
const [isSecurityPolicyConfirmOpen, setIsSecurityPolicyConfirmOpen] =
|
||||
useState(false);
|
||||
const authPageSettingsRef = useRef<AuthPageSettingsRef>(null);
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(GeneralFormSchema),
|
||||
@@ -258,14 +246,6 @@ export default function GeneralPage() {
|
||||
// Update organization
|
||||
await api.post(`/org/${org?.org.orgId}`, reqData);
|
||||
|
||||
// Also save auth page settings if they have unsaved changes
|
||||
if (
|
||||
build === "saas" &&
|
||||
authPageSettingsRef.current?.hasUnsavedChanges()
|
||||
) {
|
||||
await authPageSettingsRef.current.saveAuthSettings();
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t("orgUpdated"),
|
||||
description: t("orgUpdatedDescription")
|
||||
@@ -410,9 +390,7 @@ export default function GeneralPage() {
|
||||
{LOG_RETENTION_OPTIONS.filter(
|
||||
(option) => {
|
||||
if (
|
||||
build ==
|
||||
"saas" &&
|
||||
!subscription?.subscribed &&
|
||||
hasSaasSubscription &&
|
||||
option.value >
|
||||
30
|
||||
) {
|
||||
@@ -440,19 +418,15 @@ export default function GeneralPage() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{build != "oss" && (
|
||||
{build !== "oss" && (
|
||||
<>
|
||||
<SecurityFeaturesAlert />
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAccess"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
(build == "saas" &&
|
||||
!subscription?.subscribed) ||
|
||||
(build == "enterprise" &&
|
||||
!isUnlocked());
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
@@ -518,11 +492,7 @@ export default function GeneralPage() {
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAction"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
(build == "saas" &&
|
||||
!subscription?.subscribed) ||
|
||||
(build == "enterprise" &&
|
||||
!isUnlocked());
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
@@ -590,8 +560,7 @@ export default function GeneralPage() {
|
||||
</SettingsSectionBody>
|
||||
|
||||
{build !== "oss" && (
|
||||
<>
|
||||
<hr className="my-10 max-w-xl" />
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("securitySettings")}
|
||||
@@ -601,14 +570,13 @@ export default function GeneralPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm className="mb-4">
|
||||
<SecurityFeaturesAlert />
|
||||
<SettingsSectionForm>
|
||||
<PaidFeaturesAlert />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireTwoFactor"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
isSecurityFeatureDisabled();
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem className="col-span-2">
|
||||
@@ -655,8 +623,7 @@ export default function GeneralPage() {
|
||||
control={form.control}
|
||||
name="maxSessionLengthHours"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
isSecurityFeatureDisabled();
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem className="col-span-2">
|
||||
@@ -744,8 +711,7 @@ export default function GeneralPage() {
|
||||
control={form.control}
|
||||
name="passwordExpiryDays"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
isSecurityFeatureDisabled();
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem className="col-span-2">
|
||||
@@ -831,7 +797,7 @@ export default function GeneralPage() {
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
@@ -848,8 +814,6 @@ export default function GeneralPage() {
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
{build === "saas" && <AuthPageSettings ref={authPageSettingsRef} />}
|
||||
|
||||
{build !== "saas" && (
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
||||
Reference in New Issue
Block a user