Updated refreshing (#2327)

* clean up + add environment variables

* remove log

* update

* update api settings

* somewhat cleaner refresh functionality

* fully functional

* update settings

* validated

* remove random logs

* remove unneeded paramter + log

* move to ee + remove comments

* Cleanup unused

---------

Co-authored-by: Weves <chrisweaver101@gmail.com>
This commit is contained in:
pablodanswer
2024-09-05 21:36:55 -07:00
committed by GitHub
parent 2bd3833c55
commit 69c0419146
11 changed files with 208 additions and 23 deletions

View File

@@ -58,6 +58,7 @@ ENV NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED=${NEXT_PUBLIC_DO_NOT_USE_T
ARG NEXT_PUBLIC_DISABLE_LOGOUT
ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT}
RUN npx next build
# Step 2. Production image, copy all the files and run next

View File

@@ -91,7 +91,6 @@ import FunctionalHeader from "@/components/chat_search/Header";
import { useSidebarVisibility } from "@/components/chat_search/hooks";
import { SIDEBAR_TOGGLED_COOKIE_NAME } from "@/components/resizable/constants";
import FixedLogo from "./shared_chat_search/FixedLogo";
import { getSecondsUntilExpiration } from "@/lib/time";
import { SetDefaultModelModal } from "./modal/SetDefaultModelModal";
import { DeleteEntityModal } from "../../components/modals/DeleteEntityModal";
import { MinimalMarkdown } from "@/components/chat_search/MinimalMarkdown";
@@ -1559,7 +1558,6 @@ export function ChatPage({
setDocumentSelection((documentSelection) => !documentSelection);
setShowDocSidebar(false);
};
const secondsUntilExpiration = getSecondsUntilExpiration(user);
interface RegenerationRequest {
messageId: number;
@@ -1579,7 +1577,7 @@ export function ChatPage({
return (
<>
<HealthCheckBanner secondsUntilExpiration={secondsUntilExpiration} />
<HealthCheckBanner />
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
Only used in the EE version of the app. */}
{popup}

View File

@@ -3,7 +3,6 @@ import "./globals.css";
import {
fetchEnterpriseSettingsSS,
fetchSettingsSS,
SettingsError,
} from "@/components/settings/lib";
import {
CUSTOM_ANALYTICS_ENABLED,
@@ -11,7 +10,7 @@ import {
} from "@/lib/constants";
import { SettingsProvider } from "@/components/settings/SettingsProvider";
import { Metadata } from "next";
import { buildClientUrl } from "@/lib/utilsSS";
import { buildClientUrl, fetchSS } from "@/lib/utilsSS";
import { Inter } from "next/font/google";
import Head from "next/head";
import { EnterpriseSettings } from "./admin/settings/interfaces";

View File

@@ -3,7 +3,6 @@ import {
getAuthTypeMetadataSS,
getCurrentUserSS,
} from "@/lib/userSS";
import { getSecondsUntilExpiration } from "@/lib/time";
import { redirect } from "next/navigation";
import { HealthCheckBanner } from "@/components/health/healthcheck";
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
@@ -179,11 +178,10 @@ export default async function Home() {
const agenticSearchEnabled = agenticSearchToggle
? agenticSearchToggle.value.toLocaleLowerCase() == "true" || false
: false;
const secondsUntilExpiration = getSecondsUntilExpiration(user);
return (
<>
<HealthCheckBanner secondsUntilExpiration={secondsUntilExpiration} />
<HealthCheckBanner />
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
<InstantSSRAutoRefresh />

View File

@@ -3,29 +3,95 @@
import { errorHandlingFetcher, RedirectError } from "@/lib/fetcher";
import useSWR from "swr";
import { Modal } from "../Modal";
import { useState } from "react";
import { useEffect, useState } from "react";
import { getSecondsUntilExpiration } from "@/lib/time";
import { User } from "@/lib/types";
export const HealthCheckBanner = ({
secondsUntilExpiration,
}: {
secondsUntilExpiration?: number | null;
}) => {
export const HealthCheckBanner = () => {
const { error } = useSWR("/api/health", errorHandlingFetcher);
const [expired, setExpired] = useState(false);
const [secondsUntilExpiration, setSecondsUntilExpiration] = useState<
number | null
>(null);
const { data: user, mutate: mutateUser } = useSWR<User>(
"/api/me",
errorHandlingFetcher
);
if (secondsUntilExpiration !== null && secondsUntilExpiration !== undefined) {
setTimeout(
() => {
setExpired(true);
},
secondsUntilExpiration * 1000 - 200
);
}
const updateExpirationTime = async () => {
const updatedUser = await mutateUser();
if (updatedUser) {
const seconds = getSecondsUntilExpiration(updatedUser);
setSecondsUntilExpiration(seconds);
console.debug(`Updated seconds until expiration:! ${seconds}`);
}
};
useEffect(() => {
updateExpirationTime();
}, [user]);
useEffect(() => {
if (true) {
let refreshTimeoutId: NodeJS.Timeout;
let expireTimeoutId: NodeJS.Timeout;
const refreshToken = async () => {
try {
const response = await fetch(
"/api/enterprise-settings/refresh-token",
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
console.debug("Token refresh successful");
// Force revalidation of user data
await mutateUser(undefined, { revalidate: true });
updateExpirationTime();
} catch (error) {
console.error("Error refreshing token:", error);
}
};
const scheduleRefreshAndExpire = () => {
if (secondsUntilExpiration !== null) {
const timeUntilRefresh = (secondsUntilExpiration + 0.5) * 1000;
refreshTimeoutId = setTimeout(refreshToken, timeUntilRefresh);
const timeUntilExpire = (secondsUntilExpiration + 10) * 1000;
expireTimeoutId = setTimeout(() => {
console.debug("Session expired. Setting expired state to true.");
setExpired(true);
}, timeUntilExpire);
}
};
scheduleRefreshAndExpire();
return () => {
clearTimeout(refreshTimeoutId);
clearTimeout(expireTimeoutId);
};
}
}, [secondsUntilExpiration, user]);
if (!error && !expired) {
return null;
}
console.debug(
`Rendering HealthCheckBanner. Error: ${error}, Expired: ${expired}`
);
if (error instanceof RedirectError || expired) {
return (
<Modal

View File

@@ -101,6 +101,7 @@ export function getSecondsUntilExpiration(
if (!userInfo) {
return null;
}
const { oidc_expiry, current_token_created_at, current_token_expiry_length } =
userInfo;

View File

@@ -10,6 +10,7 @@ const eePaths = [
"/admin/whitelabeling",
"/admin/performance/custom-analytics",
];
const eePathsForMatcher = eePaths.map((path) => `${path}/:path*`);
export async function middleware(request: NextRequest) {