diff --git a/.env.development b/.env.development index 2a3613113..d5cd6b374 100644 --- a/.env.development +++ b/.env.development @@ -16,6 +16,5 @@ NEXT_PUBLIC_FT_ACTIVITIES=true SENTRY_DSN= NEXT_PUBLIC_SENTRY_DSN= -#NEXT_PUBLIC_ASKTUG_PROXY_BASE_URL=https://community-preview.tidb.net/_asktug -NEXT_PUBLIC_ASKTUG_PROXY_BASE_URL=http://127.0.0.1:3100/_asktug -NEXT_PUBLIC_ASKTUG_WEBSITE_BASE_URL=https://community-preview.asktug.com +NEXT_PUBLIC_ASKTUG_PROXY_BASE_URL=https://community-preview.tidb.net/_asktug +NEXT_PUBLIC_ASKTUG_WEBSITE_BASE_URL=https://new.asktug.com diff --git a/src/api/asktug/profile.ts b/src/api/asktug/profile.ts index cda0f7ab8..f44ca6719 100644 --- a/src/api/asktug/profile.ts +++ b/src/api/asktug/profile.ts @@ -332,10 +332,10 @@ export async function getSummaryByUsername( const { username } = input; const url = `${askTugApiDomain}/u/${encodeURIComponent(username)}/summary.json`; try { - const result: IProfileSummary = await asktugClient.get( - url, - withAccountsCookies({ isReturnErrorResponse: true }, ssrCtx) - ); + const result: IProfileSummary = await asktugClient.get(url, { + isReturnErrorResponse: true, + ssrCtx, + }); return result ?? null; } catch (response) { if (response?.status && response.status === 404) { diff --git a/src/api/clients/asktugClient.ts b/src/api/clients/asktugClient.ts index ff9bd0337..bca9d3247 100644 --- a/src/api/clients/asktugClient.ts +++ b/src/api/clients/asktugClient.ts @@ -1,5 +1,7 @@ import axios from 'axios'; import { dispatchApiError } from './events'; +import { applyDebugInterceptor } from './interceptors/debug'; +import { passThroughCookies } from '~/api/clients/interceptors/ssr'; const isDispatchGlobalApiError = (status) => { return ![400, 409, 428].includes(status); @@ -11,6 +13,7 @@ const asktugClient = axios.create({ headers: { accept: 'application/json', }, + passThroughCookies: 'asktug', isDispatchApiError({ status }) { return status !== 404; }, @@ -21,6 +24,9 @@ const asktugClient = axios.create({ // }, error => { // return Promise.reject(error); // }); +axios.interceptors.request.use(passThroughCookies); +applyDebugInterceptor(asktugClient); + asktugClient.interceptors.response.use( ({ data }) => { return data; @@ -28,8 +34,6 @@ asktugClient.interceptors.response.use( (err) => { const { config, response } = err; - console.error(response?.status ?? 'unknown', config.url); - // Some errors may not have response, like the timeout error if (!response) { dispatchApiError(err); diff --git a/src/api/clients/blogClient.js b/src/api/clients/blogClient.js index ed5101f38..c911ad878 100644 --- a/src/api/clients/blogClient.js +++ b/src/api/clients/blogClient.js @@ -1,5 +1,7 @@ import axios from 'axios'; import { dispatchApiError } from './events'; +import { applyDebugInterceptor } from './interceptors/debug'; +import { passThroughCookies } from '~/api/clients/interceptors/ssr'; const isDispatchGlobalApiError = (status) => { return ![400, 409, 428].includes(status); @@ -11,6 +13,7 @@ const blogClient = axios.create({ headers: { accept: 'application/json', }, + passThroughCookies: 'blog', }); // axios.interceptors.request.use((config) => { // console.log('request params:', config); @@ -18,6 +21,9 @@ const blogClient = axios.create({ // }, error => { // return Promise.reject(error); // }); +axios.interceptors.request.use(passThroughCookies); +applyDebugInterceptor(blogClient); + blogClient.interceptors.response.use( ({ data }) => { return data; diff --git a/src/api/clients/client.js b/src/api/clients/client.js index 1b8df73a4..bcb424b94 100644 --- a/src/api/clients/client.js +++ b/src/api/clients/client.js @@ -4,6 +4,8 @@ import { createCaptchaInterceptor } from '@tidb-community/common/utils/axios'; import { getCaptchaToken } from '@tidb-community/common/utils/form'; import { dispatchApiError } from './events'; +import { applyDebugInterceptor } from './interceptors/debug'; +import { passThroughCookies } from '~/api/clients/interceptors/ssr'; const CSRF_MSG = 'CSRF Failed: CSRF token missing or incorrect.'; @@ -14,8 +16,10 @@ const isDispatchGlobalApiError = (status) => { const client = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL ?? '', withCredentials: true, + passThroughCookies: 'accounts', }); +axios.interceptors.request.use(passThroughCookies); client.interceptors.request.use((config) => { const csrftoken = Cookie.get('csrftoken'); @@ -33,6 +37,8 @@ client.interceptors.request.use((config) => { return config; }); +applyDebugInterceptor(client); + client.interceptors.response.use( ({ data }) => data, (err) => { diff --git a/src/api/clients/index.ts b/src/api/clients/index.ts index 0c71717ac..c83369849 100644 --- a/src/api/clients/index.ts +++ b/src/api/clients/index.ts @@ -1,17 +1,21 @@ import { AxiosInstance, AxiosResponse } from 'axios'; +import _asktugClient from './asktugClient'; import _blogClient from './blogClient'; import _client from './client'; import _nextClient from './nextClient'; +import { GetServerSidePropsContext } from 'next'; declare module 'axios' { export interface AxiosRequestConfig { isDispatchApiError?(res: AxiosResponse): boolean; isReturnErrorResponse?: boolean; fallbackResponse?: any; + ssrCtx?: GetServerSidePropsContext; + passThroughCookies?: 'accounts' | 'asktug' | 'blog'; } } -export { default as asktugClient } from './asktugClient'; +export const asktugClient: AxiosInstance = _asktugClient; export const blogClient: AxiosInstance = _blogClient; export const nextClient: AxiosInstance = _nextClient; export const client: AxiosInstance = _client; diff --git a/src/api/clients/interceptors/debug.ts b/src/api/clients/interceptors/debug.ts new file mode 100644 index 000000000..784d91d8a --- /dev/null +++ b/src/api/clients/interceptors/debug.ts @@ -0,0 +1,32 @@ +import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; + +function isAxiosError(error: unknown): error is AxiosError { + return error && typeof error === 'object' && (error as AxiosError).isAxiosError; +} + +export function debugResponseInterceptor(response: AxiosResponse): AxiosResponse { + // eslint-disable-next-line no-console + console.debug('[axios:debug:success]', response.status, response.config.method, response.request.path); + return response; +} + +export function debugErrorResponseInterceptor(error: unknown) { + if (!error) { + throw error; + } + if (isAxiosError(error)) { + let errorPrefix = `[axios:debug:error]`; + if (error.config.ssrCtx) { + errorPrefix += ' at ' + error.config.ssrCtx.resolvedUrl + ':'; + } + console.error(errorPrefix, error.response.status, error.config.method, error.response.request.path); + } + throw error; +} + +export function applyDebugInterceptor(client: AxiosInstance) { + if (process.env.NODE_ENV === 'development' || process.env.AXIOS_DEBUG === 'true') { + client.interceptors.response.use(debugResponseInterceptor); + } + client.interceptors.response.use(undefined, debugErrorResponseInterceptor); +} diff --git a/src/api/clients/interceptors/ssr.ts b/src/api/clients/interceptors/ssr.ts new file mode 100644 index 000000000..fc4e0cba3 --- /dev/null +++ b/src/api/clients/interceptors/ssr.ts @@ -0,0 +1,31 @@ +import { AxiosRequestConfig } from 'axios'; + +const SITE_COOKIES = { + accounts: ['uid', 'dev_sid', 'ssid', '_t'], + asktug: ['uid', 'dev_sid', 'ssid', '_t'], + blog: ['uid', 'dev_sid', 'ssid', 'JSESSIONID'], +}; + +export function passThroughCookies(config: AxiosRequestConfig): AxiosRequestConfig { + const { passThroughCookies, ssrCtx } = config; + if (!passThroughCookies || !ssrCtx) { + return config; + } + + const cookies = ssrCtx.req.cookies; + const headers = (config.headers = config.headers || {}); + + if (headers.cookie) { + headers.cookie += ';'; + } else { + headers.cookie = ''; + } + + headers.cookie += SITE_COOKIES[passThroughCookies] + .map((name) => [name, cookies[name]]) + .filter(([, value]) => value) + .map(([name, value]) => `${name}=${encodeURIComponent(value)}`) + .join(';'); + + return config; +} diff --git a/src/pages/u/[username]/answer/[[...status]].page.tsx b/src/pages/u/[username]/answer/[[...status]].page.tsx index 9fce0c0cf..f725954d8 100644 --- a/src/pages/u/[username]/answer/[[...status]].page.tsx +++ b/src/pages/u/[username]/answer/[[...status]].page.tsx @@ -60,7 +60,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx getI18nProps(['common'])(ctx), getBadgesByUsername({ username }), getUserProfileByUsername({ username }), - getSummaryByUsername({ username }), + getSummaryByUsername({ username }, ctx), getAnswersByUsername({ pageNumber: pageNumber + 1, pageSize, username, markedSolution }), getPostsNumberByUsername({ username }), getPostFavoritesNumberByUsername({ username }),