From 8dbca18e1754ae4da3e5aa4162b3ee1235dc03f6 Mon Sep 17 00:00:00 2001 From: Kyle9410-Chen Date: Tue, 24 Feb 2026 23:00:51 +0800 Subject: [PATCH 1/2] fix: implement auth token refresh logic and remove obsolete hook --- src/features/auth/hooks/useAuth.ts | 16 ++++++++++ src/layouts/AdminLayout.tsx | 5 +-- src/layouts/UserLayout.tsx | 2 +- src/layouts/useAuthRefreshInterval.ts | 46 --------------------------- 4 files changed, 20 insertions(+), 49 deletions(-) delete mode 100644 src/layouts/useAuthRefreshInterval.ts diff --git a/src/features/auth/hooks/useAuth.ts b/src/features/auth/hooks/useAuth.ts index 9e1c3f4..c6b7674 100644 --- a/src/features/auth/hooks/useAuth.ts +++ b/src/features/auth/hooks/useAuth.ts @@ -1,6 +1,10 @@ import { authService } from "@/features/auth/services/authService"; import type { UserOnboardingRequest } from "@nycu-sdc/core-system-sdk"; +import { authRefreshToken } from "@nycu-sdc/core-system-sdk"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useEffect } from "react"; + +const DEFAULT_AUTH_REFRESH_INTERVAL = 5 * 60 * 1000; export const useMe = () => useQuery({ @@ -30,3 +34,15 @@ export const useAuth = () => { isLoading }; }; + +export const useAuthRefreshInterval = () => { + useEffect(() => { + console.log("Setting up auth refresh interval"); + const interval = setInterval(() => { + console.log("Refreshing auth token"); + authRefreshToken(""); + }, DEFAULT_AUTH_REFRESH_INTERVAL); + + return () => clearInterval(interval); + }, []); +}; diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index ae5bda0..db36994 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -1,17 +1,18 @@ +import { useAuthRefreshInterval } from "@/features/auth/hooks/useAuth"; import { Footer } from "@/shared/components/Footer/Footer"; import type { ReactNode } from "react"; import { useState } from "react"; import styles from "./AdminLayout.module.css"; import { AdminNav } from "./AdminNav"; -import { useAuthRefreshInterval } from "./useAuthRefreshInterval"; interface AdminLayoutProps { children: ReactNode; } export const AdminLayout = ({ children }: AdminLayoutProps) => { - useAuthRefreshInterval(); const [isNavOpen, setIsNavOpen] = useState(false); + useAuthRefreshInterval(); + return (
diff --git a/src/layouts/UserLayout.tsx b/src/layouts/UserLayout.tsx index d309034..8cc1c63 100644 --- a/src/layouts/UserLayout.tsx +++ b/src/layouts/UserLayout.tsx @@ -1,7 +1,7 @@ +import { useAuthRefreshInterval } from "@/features/auth/hooks/useAuth"; import { Footer } from "@/shared/components/Footer/Footer"; import type { ReactNode } from "react"; import styles from "./UserLayout.module.css"; -import { useAuthRefreshInterval } from "./useAuthRefreshInterval"; interface UserLayoutProps { children: ReactNode; diff --git a/src/layouts/useAuthRefreshInterval.ts b/src/layouts/useAuthRefreshInterval.ts deleted file mode 100644 index 9fe4c10..0000000 --- a/src/layouts/useAuthRefreshInterval.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { authService } from "@/features/auth/services/authService"; -import { useQueryClient } from "@tanstack/react-query"; -import { useEffect } from "react"; - -const REFRESH_INTERVAL_MS = 3 * 60 * 1000; - -export const useAuthRefreshInterval = () => { - const queryClient = useQueryClient(); - - useEffect(() => { - let refreshingPromise: Promise | null = null; - - const refreshAuth = async () => { - if (!authService.getStoredRefreshToken()) return; - if (refreshingPromise) { - await refreshingPromise; - return; - } - - refreshingPromise = (async () => { - try { - const refreshed = await authService.refreshAccessToken(); - if (refreshed) { - queryClient.invalidateQueries({ queryKey: ["user", "me"] }); - } - } catch { - queryClient.setQueryData(["user", "me"], null); - } finally { - refreshingPromise = null; - } - })(); - - await refreshingPromise; - }; - - const intervalId = window.setInterval(() => { - void refreshAuth(); - }, REFRESH_INTERVAL_MS); - - void refreshAuth(); - - return () => { - window.clearInterval(intervalId); - }; - }, [queryClient]); -}; From 7a50fd276b3f581a9f4c89ab6a2ca655c44e0305 Mon Sep 17 00:00:00 2001 From: Kyle9410-Chen Date: Tue, 24 Feb 2026 23:12:03 +0800 Subject: [PATCH 2/2] fix: remove obsolete refresh token logic and clean up related code --- src/features/auth/components/CallbackPage.tsx | 10 -- src/features/auth/hooks/useAuth.ts | 2 - src/features/auth/services/authService.ts | 103 +----------------- src/features/dashboard/services/api.ts | 17 ++- .../AdminFormDetailPages/EditPage.tsx | 2 - src/features/form/services/api.ts | 61 +++++------ 6 files changed, 42 insertions(+), 153 deletions(-) diff --git a/src/features/auth/components/CallbackPage.tsx b/src/features/auth/components/CallbackPage.tsx index 697229e..8192fb4 100644 --- a/src/features/auth/components/CallbackPage.tsx +++ b/src/features/auth/components/CallbackPage.tsx @@ -23,11 +23,6 @@ function getSafeRedirectTarget(): string | null { } } -function getRefreshTokenFromUrl(): string | null { - const params = new URLSearchParams(window.location.search); - return params.get("refreshToken") ?? params.get("rt"); -} - export const CallbackPage = () => { const navigate = useNavigate(); const queryClient = useQueryClient(); @@ -38,11 +33,6 @@ export const CallbackPage = () => { const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); try { - const refreshToken = getRefreshTokenFromUrl(); - if (refreshToken) { - authService.setStoredRefreshToken(refreshToken); - } - let user: AuthUser | null = null; for (let attempt = 0; attempt < 3; attempt += 1) { diff --git a/src/features/auth/hooks/useAuth.ts b/src/features/auth/hooks/useAuth.ts index c6b7674..da26479 100644 --- a/src/features/auth/hooks/useAuth.ts +++ b/src/features/auth/hooks/useAuth.ts @@ -37,9 +37,7 @@ export const useAuth = () => { export const useAuthRefreshInterval = () => { useEffect(() => { - console.log("Setting up auth refresh interval"); const interval = setInterval(() => { - console.log("Refreshing auth token"); authRefreshToken(""); }, DEFAULT_AUTH_REFRESH_INTERVAL); diff --git a/src/features/auth/services/authService.ts b/src/features/auth/services/authService.ts index 9a0644a..75fb7d9 100644 --- a/src/features/auth/services/authService.ts +++ b/src/features/auth/services/authService.ts @@ -1,6 +1,6 @@ import { assertOk } from "@/shared/utils/apiError"; import type { UserOnboardingRequest, UserUser } from "@nycu-sdc/core-system-sdk"; -import { authLogout, authRefreshToken, userGetMe, userUpdateUsername } from "@nycu-sdc/core-system-sdk"; +import { authLogout, userGetMe, userUpdateUsername } from "@nycu-sdc/core-system-sdk"; export type OAuthProvider = "google" | "nycu"; @@ -10,105 +10,11 @@ const defaultRequestOptions: RequestInit = { credentials: "include" }; -const REFRESH_TOKEN_STORAGE_KEY = "core-system.refresh-token"; -const ACCESS_TOKEN_EXPIRY_STORAGE_KEY = "core-system.access-token-exp-ms"; -let refreshInFlight: Promise | null = null; - -const parseJwtExpirationMs = (jwt: string): number | null => { - try { - const [, payloadBase64Url] = jwt.split("."); - if (!payloadBase64Url) return null; - const payloadBase64 = payloadBase64Url.replace(/-/g, "+").replace(/_/g, "/"); - const padded = payloadBase64 + "=".repeat((4 - (payloadBase64.length % 4)) % 4); - const payloadJson = atob(padded); - const payload = JSON.parse(payloadJson) as { exp?: number }; - if (!payload.exp || typeof payload.exp !== "number") return null; - return payload.exp * 1000; - } catch { - return null; - } -}; - -export const withAuthRefreshRetry = async (request: () => Promise): Promise => { - const firstResponse = await request(); - if (firstResponse.status !== 401) return firstResponse; - - if (!refreshInFlight) { - refreshInFlight = authService - .refreshAccessToken() - .catch(() => false) - .finally(() => { - refreshInFlight = null; - }); - } - - const refreshed = await refreshInFlight; - if (!refreshed) return firstResponse; - - return request(); -}; - const normalizeProvider = (provider: OAuthProvider): string => { return provider.toLowerCase(); }; export const authService = { - getStoredRefreshToken(): string | null { - if (typeof window === "undefined") return null; - return window.localStorage.getItem(REFRESH_TOKEN_STORAGE_KEY); - }, - - setStoredRefreshToken(refreshToken: string) { - if (typeof window === "undefined") return; - window.localStorage.setItem(REFRESH_TOKEN_STORAGE_KEY, refreshToken); - }, - - getStoredAccessTokenExpiryMs(): number | null { - if (typeof window === "undefined") return null; - const raw = window.localStorage.getItem(ACCESS_TOKEN_EXPIRY_STORAGE_KEY); - if (!raw) return null; - const value = Number(raw); - return Number.isFinite(value) ? value : null; - }, - - setStoredAccessTokenExpiryMs(expirationMs: number) { - if (typeof window === "undefined") return; - window.localStorage.setItem(ACCESS_TOKEN_EXPIRY_STORAGE_KEY, String(expirationMs)); - }, - - clearStoredAccessTokenExpiryMs() { - if (typeof window === "undefined") return; - window.localStorage.removeItem(ACCESS_TOKEN_EXPIRY_STORAGE_KEY); - }, - - clearStoredRefreshToken() { - if (typeof window === "undefined") return; - window.localStorage.removeItem(REFRESH_TOKEN_STORAGE_KEY); - this.clearStoredAccessTokenExpiryMs(); - }, - - async refreshAccessToken(): Promise { - const refreshToken = this.getStoredRefreshToken(); - if (!refreshToken) return false; - - const res = await authRefreshToken(refreshToken, defaultRequestOptions); - if (res.status === 404) { - this.clearStoredRefreshToken(); - throw new Error("Refresh token expired"); - } - - assertOk(res.status, "Failed to refresh access token", res.data); - if (res.data?.refreshToken) { - this.setStoredRefreshToken(res.data.refreshToken); - } - const accessTokenExpMs = parseJwtExpirationMs(res.data.accessToken); - if (accessTokenExpMs) { - this.setStoredAccessTokenExpiryMs(accessTokenExpMs); - } - - return true; - }, - redirectToOAuthLogin( provider: OAuthProvider, options: { @@ -132,19 +38,18 @@ export const authService = { }, async logout(): Promise { - const res = await withAuthRefreshRetry(() => authLogout(defaultRequestOptions)); + const res = await authLogout(defaultRequestOptions); assertOk(res.status, "Failed to logout", res.data); - this.clearStoredRefreshToken(); }, async getCurrentUser(): Promise { - const res = await withAuthRefreshRetry(() => userGetMe(defaultRequestOptions)); + const res = await userGetMe(defaultRequestOptions); assertOk(res.status, "Failed to get current user", res.data); return res.data as T; }, async updateOnboarding(data: UserOnboardingRequest): Promise { - const res = await withAuthRefreshRetry(() => userUpdateUsername(data, defaultRequestOptions)); + const res = await userUpdateUsername(data, defaultRequestOptions); assertOk(res.status, "Failed to update onboarding", res.data); } }; diff --git a/src/features/dashboard/services/api.ts b/src/features/dashboard/services/api.ts index dc2ca32..08c46f7 100644 --- a/src/features/dashboard/services/api.ts +++ b/src/features/dashboard/services/api.ts @@ -1,4 +1,3 @@ -import { withAuthRefreshRetry } from "@/features/auth/services/authService"; import { assertOk } from "@/shared/utils/apiError"; import type { SlugGetSlugHistory200, SlugStatus, UnitOrgMemberRequest, UnitOrganization, UnitUpdateOrgRequest } from "@nycu-sdc/core-system-sdk"; import { @@ -17,48 +16,48 @@ const defaultRequestOptions: RequestInit = { }; export const getOrg = async (slug: string): Promise => { - const res = await withAuthRefreshRetry(() => unitGetOrgById(slug, defaultRequestOptions)); + const res = await unitGetOrgById(slug, defaultRequestOptions); assertOk(res.status, "Failed to load organization", res.data); return res.data; }; export const listOrgMembers = async (slug: string) => { - const res = await withAuthRefreshRetry(() => unitListOrgMembers(slug, defaultRequestOptions)); + const res = await unitListOrgMembers(slug, defaultRequestOptions); assertOk(res.status, "Failed to load members", res.data); return res.data; }; export const updateOrg = async (slug: string, req: UnitUpdateOrgRequest) => { - const res = await withAuthRefreshRetry(() => unitUpdateOrg(slug, req, defaultRequestOptions)); + const res = await unitUpdateOrg(slug, req, defaultRequestOptions); assertOk(res.status, "Failed to update organization", res.data); return res.data; }; export const addOrgMember = async (slug: string, req: UnitOrgMemberRequest) => { - const res = await withAuthRefreshRetry(() => unitAddOrgMember(slug, req, defaultRequestOptions)); + const res = await unitAddOrgMember(slug, req, defaultRequestOptions); assertOk(res.status, "Failed to add member", res.data); return res.data; }; export const removeOrgMember = async (slug: string, memberId: string): Promise => { - const res = await withAuthRefreshRetry(() => unitRemoveOrgMember(slug, memberId, defaultRequestOptions)); + const res = await unitRemoveOrgMember(slug, memberId, defaultRequestOptions); assertOk(res.status, "Failed to remove member", res.data); }; export const listMyOrgs = async (): Promise => { - const res = await withAuthRefreshRetry(() => unitListOrganizationsOfCurrentUser(defaultRequestOptions)); + const res = await unitListOrganizationsOfCurrentUser(defaultRequestOptions); assertOk(res.status, "Failed to load my organizations", res.data); return res.data; }; export const getSlugStatus = async (slug: string): Promise => { - const res = await withAuthRefreshRetry(() => slugGetSlugStatus(slug, defaultRequestOptions)); + const res = await slugGetSlugStatus(slug, defaultRequestOptions); assertOk(res.status, "Failed to get slug status", res.data); return res.data as SlugStatus; }; export const getSlugHistory = async (slug: string): Promise => { - const res = await withAuthRefreshRetry(() => slugGetSlugHistory(slug, defaultRequestOptions)); + const res = await slugGetSlugHistory(slug, defaultRequestOptions); assertOk(res.status, "Failed to get slug history", res.data); return res.data as SlugGetSlugHistory200; }; diff --git a/src/features/form/components/AdminFormDetailPages/EditPage.tsx b/src/features/form/components/AdminFormDetailPages/EditPage.tsx index 72329ba..16f2814 100644 --- a/src/features/form/components/AdminFormDetailPages/EditPage.tsx +++ b/src/features/form/components/AdminFormDetailPages/EditPage.tsx @@ -92,8 +92,6 @@ export const AdminFormEditPage = ({ formData }: AdminFormEditPageProps) => { isMergeNode: pass1.filter(n => n.next === node.id).length + pass1.filter(n => n.nextTrue === node.id).length + pass1.filter(n => n.nextFalse === node.id).length > 1 })); - console.log("Post-processed nodes:", res); - return res; }; diff --git a/src/features/form/services/api.ts b/src/features/form/services/api.ts index 2ced7ea..8bfdb1f 100644 --- a/src/features/form/services/api.ts +++ b/src/features/form/services/api.ts @@ -1,4 +1,3 @@ -import { withAuthRefreshRetry } from "@/features/auth/services/authService"; import { assertOk } from "@/shared/utils/apiError"; import type { FormWorkflowCreateNodeRequest, @@ -64,55 +63,55 @@ const defaultRequestOptions: RequestInit = { // ── Forms ────────────────────────────────────────────────────────────────── export const listOrgForms = async (slug: string): Promise => { - const res = await withAuthRefreshRetry(() => unitListFormsByOrg(slug, defaultRequestOptions)); + const res = await unitListFormsByOrg(slug, defaultRequestOptions); assertOk(res.status, "Failed to load forms", res.data); return res.data; }; export const createOrgForm = async (slug: string, req: FormsFormRequest): Promise => { - const res = await withAuthRefreshRetry(() => unitCreateOrgForm(slug, req, defaultRequestOptions)); + const res = await unitCreateOrgForm(slug, req, defaultRequestOptions); assertOk(res.status, "Failed to create form", res.data); return res.data; }; export const getFormById = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsGetFormById(formId, defaultRequestOptions)); + const res = await formsGetFormById(formId, defaultRequestOptions); assertOk(res.status, "Failed to load form", res.data); return res.data; }; export const updateForm = async (formId: string, req: FormsFormRequestUpdate): Promise => { - const res = await withAuthRefreshRetry(() => formsUpdateForm(formId, req, defaultRequestOptions)); + const res = await formsUpdateForm(formId, req, defaultRequestOptions); assertOk(res.status, "Failed to update form", res.data); return res.data; }; export const publishForm = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsPublishForm(formId, defaultRequestOptions)); + const res = await formsPublishForm(formId, defaultRequestOptions); assertOk(res.status, "Failed to publish form", res.data); // publishForm returns FormPublishResponse which has same shape as Form return res.data as unknown as FormsForm; }; export const archiveForm = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsArchiveForm(formId, defaultRequestOptions)); + const res = await formsArchiveForm(formId, defaultRequestOptions); assertOk(res.status, "Failed to archive form", res.data); return res.data; }; export const deleteForm = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsDeleteForm(formId, defaultRequestOptions)); + const res = await formsDeleteForm(formId, defaultRequestOptions); assertOk(res.status, "Failed to delete form", res.data); }; export const uploadFormCoverImage = async (formId: string, file: File): Promise => { - const res = await withAuthRefreshRetry(() => formsUploadFormCoverImage(formId, { coverImage: file }, defaultRequestOptions)); + const res = await formsUploadFormCoverImage(formId, { coverImage: file }, defaultRequestOptions); assertOk(res.status, "Failed to upload cover image", res.data); return res.data; }; export const getFormFonts = async (): Promise => { - const res = await withAuthRefreshRetry(() => formsGetFormFonts(defaultRequestOptions)); + const res = await formsGetFormFonts(defaultRequestOptions); assertOk(res.status, "Failed to load fonts", res.data); return res.data; }; @@ -120,7 +119,7 @@ export const getFormFonts = async (): Promise => { // ── Sections & Questions ────────────────────────────────────────────────── export const listSections = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsListSections(formId, defaultRequestOptions)); + const res = await formsListSections(formId, defaultRequestOptions); assertOk(res.status, "Failed to load sections", res.data); const raw = res.data as unknown; @@ -139,57 +138,57 @@ export const listSections = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsUpdateSection(formId, sectionId, req, defaultRequestOptions)); + const res = await formsUpdateSection(formId, sectionId, req, defaultRequestOptions); assertOk(res.status, "Failed to update section", res.data); return res.data; }; export const createQuestion = async (sectionId: string, req: FormsQuestionRequest): Promise => { - const res = await withAuthRefreshRetry(() => formsCreateQuestion(sectionId, req, defaultRequestOptions)); + const res = await formsCreateQuestion(sectionId, req, defaultRequestOptions); assertOk(res.status, "Failed to create question", res.data); return res.data; }; export const updateQuestion = async (sectionId: string, questionId: string, req: FormsQuestionRequest): Promise => { - const res = await withAuthRefreshRetry(() => formsUpdateQuestion(sectionId, questionId, req, defaultRequestOptions)); + const res = await formsUpdateQuestion(sectionId, questionId, req, defaultRequestOptions); assertOk(res.status, "Failed to update question", res.data); return res.data; }; export const deleteQuestion = async (sectionId: string, questionId: string): Promise => { - const res = await withAuthRefreshRetry(() => formsDeleteQuestion(sectionId, questionId, defaultRequestOptions)); + const res = await formsDeleteQuestion(sectionId, questionId, defaultRequestOptions); assertOk(res.status, "Failed to delete question", res.data); }; // ── Workflow ────────────────────────────────────────────────────────────── export const getWorkflow = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => formWorkflowGetWorkflow(formId, defaultRequestOptions)); + const res = await formWorkflowGetWorkflow(formId, defaultRequestOptions); assertOk(res.status, "Failed to load workflow", res.data); return res.data as FormWorkflowGetWorkflowResponse; }; export const updateWorkflow = async (formId: string, nodes: FormWorkflowNodeRequest[]): Promise => { - const res = await withAuthRefreshRetry(() => formWorkflowUpdateWorkflow(formId, nodes, defaultRequestOptions)); + const res = await formWorkflowUpdateWorkflow(formId, nodes, defaultRequestOptions); assertOk(res.status, "Failed to update workflow", res.data); return res.data as FormWorkflowNodeResponse[]; }; export const createWorkflowNode = async (formId: string, req: FormWorkflowCreateNodeRequest): Promise => { - const res = await withAuthRefreshRetry(() => formWorkflowCreateNode(formId, req, defaultRequestOptions)); + const res = await formWorkflowCreateNode(formId, req, defaultRequestOptions); assertOk(res.status, "Failed to create workflow node", res.data); return res.data as FormWorkflowNodeStructure; }; export const deleteWorkflowNode = async (formId: string, nodeId: string): Promise => { - const res = await withAuthRefreshRetry(() => formWorkflowDeleteNode(formId, nodeId, defaultRequestOptions)); + const res = await formWorkflowDeleteNode(formId, nodeId, defaultRequestOptions); assertOk(res.status, "Failed to delete workflow node", res.data); }; // ── User Forms ──────────────────────────────────────────────────────────── export const listMyForms = async (): Promise => { - const res = await withAuthRefreshRetry(() => unitListFormsOfCurrentUser(defaultRequestOptions)); + const res = await unitListFormsOfCurrentUser(defaultRequestOptions); assertOk(res.status, "Failed to load my forms", res.data); return res.data; }; @@ -197,40 +196,40 @@ export const listMyForms = async (): Promise => { // ── Responses ───────────────────────────────────────────────────────────── export const createFormResponse = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => responsesCreateFormResponse(formId, defaultRequestOptions)); + const res = await responsesCreateFormResponse(formId, defaultRequestOptions); assertOk(res.status, "Failed to start form", res.data); return res.data as ResponsesCreateResponse; }; export const listFormResponses = async (formId: string): Promise => { - const res = await withAuthRefreshRetry(() => responsesListFormResponses(formId, defaultRequestOptions)); + const res = await responsesListFormResponses(formId, defaultRequestOptions); assertOk(res.status, "Failed to load responses", res.data); return res.data as ResponsesListResponse; }; export const getFormResponse = async (formId: string, responseId: string): Promise => { - const res = await withAuthRefreshRetry(() => responsesGetFormResponse(formId, responseId, defaultRequestOptions)); + const res = await responsesGetFormResponse(formId, responseId, defaultRequestOptions); assertOk(res.status, "Failed to load response", res.data); return res.data as ResponsesGetFormResponse; }; export const deleteFormResponse = async (formId: string, responseId: string): Promise => { - const res = await withAuthRefreshRetry(() => responsesDeleteFormResponse(formId, responseId, defaultRequestOptions)); + const res = await responsesDeleteFormResponse(formId, responseId, defaultRequestOptions); assertOk(res.status, "Failed to delete response", res.data); }; export const updateFormResponse = async (responseId: string, answers: import("@nycu-sdc/core-system-sdk").ResponsesAnswersRequestUpdate): Promise => { - const res = await withAuthRefreshRetry(() => responsesUpdateFormResponse(responseId, answers, defaultRequestOptions)); + const res = await responsesUpdateFormResponse(responseId, answers, defaultRequestOptions); assertOk(res.status, "Failed to save answers", res.data); }; export const submitFormResponse = async (responseId: string, answers: import("@nycu-sdc/core-system-sdk").ResponsesAnswersRequest): Promise => { - const res = await withAuthRefreshRetry(() => responsesSubmitFormResponse(responseId, answers, defaultRequestOptions)); + const res = await responsesSubmitFormResponse(responseId, answers, defaultRequestOptions); assertOk(res.status, "Failed to submit form", res.data); }; export const uploadQuestionFiles = async (responseId: string, questionId: string, files: File[]): Promise => { - const res = await withAuthRefreshRetry(async () => { + const res = await (async () => { const formData = new FormData(); files.forEach(file => formData.append("file", file)); const response = await fetch(`/api/responses/${responseId}/questions/${questionId}/files`, { @@ -241,13 +240,13 @@ export const uploadQuestionFiles = async (responseId: string, questionId: string const body = [204, 205, 304].includes(response.status) ? null : await response.text(); const data = body ? JSON.parse(body) : {}; return { data, status: response.status, headers: response.headers }; - }); + })(); assertOk(res.status, "Failed to upload files", res.data); return res.data as ResponsesQuestionFilesUploadResponse; }; export const getQuestionResponse = async (responseId: string, questionId: string): Promise => { - const res = await withAuthRefreshRetry(() => responsesGetQuestionResponse(responseId, questionId, defaultRequestOptions)); + const res = await responsesGetQuestionResponse(responseId, questionId, defaultRequestOptions); assertOk(res.status, "Failed to get question response", res.data); return res.data as ResponsesGetQuestionResponse; }; @@ -263,13 +262,13 @@ export const getConnectOauthAccountUrl = (responseId: string, questionId: string // ── Google Sheet ────────────────────────────────────────────────────────── export const getGoogleSheetEmail = async (): Promise => { - const res = await withAuthRefreshRetry(() => formsGetGoogleSheetEmail(defaultRequestOptions)); + const res = await formsGetGoogleSheetEmail(defaultRequestOptions); assertOk(res.status, "Failed to load Google Sheet email", res.data); return res.data; }; export const verifyGoogleSheet = async (req: FormsGoogleSheetVerifyRequest): Promise => { - const res = await withAuthRefreshRetry(() => formsVerifyGoogleSheet(req, defaultRequestOptions)); + const res = await formsVerifyGoogleSheet(req, defaultRequestOptions); assertOk(res.status, "Failed to verify Google Sheet", res.data); return res.data; };