mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-27 04:18:35 +02:00
Custom Refresh on Client Side (#2376)
This commit is contained in:
@@ -58,6 +58,9 @@ 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}
|
||||
|
||||
ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL
|
||||
ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL}
|
||||
|
||||
|
||||
RUN npx next build
|
||||
|
||||
@@ -116,6 +119,9 @@ ENV NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN=${NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN}
|
||||
ARG NEXT_PUBLIC_DISABLE_LOGOUT
|
||||
ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT}
|
||||
|
||||
ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL
|
||||
ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL}
|
||||
|
||||
# Note: Don't expose ports here, Compose will handle that for us if necessary.
|
||||
# If you want to run this without compose, specify the ports to
|
||||
# expose via cli
|
||||
|
@@ -6,6 +6,8 @@ import { Modal } from "../Modal";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getSecondsUntilExpiration } from "@/lib/time";
|
||||
import { User } from "@/lib/types";
|
||||
import { mockedRefreshToken, refreshToken } from "./refreshUtils";
|
||||
import { CUSTOM_REFRESH_URL } from "@/lib/constants";
|
||||
|
||||
export const HealthCheckBanner = () => {
|
||||
const { error } = useSWR("/api/health", errorHandlingFetcher);
|
||||
@@ -33,27 +35,32 @@ export const HealthCheckBanner = () => {
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (true) {
|
||||
if (CUSTOM_REFRESH_URL) {
|
||||
const refreshUrl = CUSTOM_REFRESH_URL;
|
||||
let refreshTimeoutId: NodeJS.Timeout;
|
||||
let expireTimeoutId: NodeJS.Timeout;
|
||||
|
||||
const refreshToken = async () => {
|
||||
const attemptTokenRefresh = async () => {
|
||||
try {
|
||||
// NOTE: This is a mocked refresh token for testing purposes.
|
||||
// const refreshTokenData = mockedRefreshToken();
|
||||
|
||||
const refreshTokenData = await refreshToken(refreshUrl);
|
||||
|
||||
const response = await fetch(
|
||||
"/api/enterprise-settings/refresh-token",
|
||||
{
|
||||
method: "GET",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(refreshTokenData),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
console.debug("Token refresh successful");
|
||||
// Force revalidation of user data
|
||||
await new Promise((resolve) => setTimeout(resolve, 4000));
|
||||
|
||||
await mutateUser(undefined, { revalidate: true });
|
||||
updateExpirationTime();
|
||||
@@ -65,7 +72,7 @@ export const HealthCheckBanner = () => {
|
||||
const scheduleRefreshAndExpire = () => {
|
||||
if (secondsUntilExpiration !== null) {
|
||||
const timeUntilRefresh = (secondsUntilExpiration + 0.5) * 1000;
|
||||
refreshTimeoutId = setTimeout(refreshToken, timeUntilRefresh);
|
||||
refreshTimeoutId = setTimeout(attemptTokenRefresh, timeUntilRefresh);
|
||||
|
||||
const timeUntilExpire = (secondsUntilExpiration + 10) * 1000;
|
||||
expireTimeoutId = setTimeout(() => {
|
||||
|
59
web/src/components/health/refreshUtils.ts
Normal file
59
web/src/components/health/refreshUtils.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { User } from "@/lib/types";
|
||||
|
||||
export interface CustomRefreshTokenResponse {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
session: {
|
||||
exp: number;
|
||||
};
|
||||
userinfo: {
|
||||
sub: string;
|
||||
familyName: string;
|
||||
givenName: string;
|
||||
fullName: string;
|
||||
userId: string;
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function mockedRefreshToken(): CustomRefreshTokenResponse {
|
||||
/**
|
||||
* This function mocks the response from a token refresh endpoint.
|
||||
* It generates a mock access token, refresh token, and user information
|
||||
* with an expiration time set to 1 hour from now.
|
||||
* This is useful for testing or development when the actual refresh endpoint is not available.
|
||||
*/
|
||||
const mockExp = Date.now() + 3600000; // 1 hour from now in milliseconds
|
||||
const data: CustomRefreshTokenResponse = {
|
||||
access_token: "asdf Mock access token",
|
||||
refresh_token: "asdf Mock refresh token",
|
||||
session: { exp: mockExp },
|
||||
userinfo: {
|
||||
sub: "Mock email",
|
||||
familyName: "Mock name",
|
||||
givenName: "Mock name",
|
||||
fullName: "Mock name",
|
||||
userId: "Mock User ID",
|
||||
email: "email@danswer.ai",
|
||||
},
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function refreshToken(
|
||||
customRefreshUrl: string
|
||||
): Promise<CustomRefreshTokenResponse> {
|
||||
try {
|
||||
console.debug("Sending request to custom refresh URL");
|
||||
const url = new URL(customRefreshUrl);
|
||||
url.searchParams.append("info", "json");
|
||||
url.searchParams.append("access_token_refresh_interval", "3600");
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error refreshing token:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
@@ -36,6 +36,7 @@ export const NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN =
|
||||
export const TOGGLED_CONNECTORS_COOKIE_NAME = "toggled_connectors";
|
||||
|
||||
/* Enterprise-only settings */
|
||||
export const CUSTOM_REFRESH_URL = process.env.NEXT_PUBLIC_CUSTOM_REFRESH_URL;
|
||||
|
||||
// NOTE: this should ONLY be used on the server-side. If used client side,
|
||||
// it will not be accurate (will always be false).
|
||||
|
Reference in New Issue
Block a user