fix: upgrade better-auth client compatibility

This commit is contained in:
DIYgod 2026-03-16 21:14:41 +08:00
parent b55e2f0eb7
commit 397049014f
16 changed files with 787 additions and 170 deletions

View File

@ -16,6 +16,30 @@ export const injectRecaptchaToken = async (page: Page, env?: DesktopE2EEnv) => {
(nextEnv) => {
window.__FOLO_E2E_RECAPTCHA_TOKEN__ = "e2e-token"
const originalFetch = globalThis.fetch.bind(globalThis)
const authEndpoints = [
"/better-auth/sign-in/email",
"/better-auth/sign-up/email",
"/better-auth/forget-password",
]
globalThis.fetch = async (input, init) => {
const request = input instanceof Request ? input : new Request(input, init)
const requestURL = new URL(request.url, globalThis.location.origin)
const shouldInjectToken = authEndpoints.some((path) => requestURL.pathname.includes(path))
if (!shouldInjectToken) {
return originalFetch(input, init)
}
const headers = new Headers(request.headers)
if (!headers.has("x-token")) {
headers.set("x-token", "r3:e2e-token")
}
return originalFetch(new Request(request, { headers }))
}
if (!nextEnv) {
return
}

View File

@ -51,6 +51,30 @@ export const launchElectronApp = async (env: DesktopE2EEnv) => {
await page.waitForLoadState("domcontentloaded")
await page.evaluate(() => {
window.__FOLO_E2E_RECAPTCHA_TOKEN__ = "e2e-token"
const originalFetch = globalThis.fetch.bind(globalThis)
const authEndpoints = [
"/better-auth/sign-in/email",
"/better-auth/sign-up/email",
"/better-auth/forget-password",
]
globalThis.fetch = async (input, init) => {
const request = input instanceof Request ? input : new Request(input, init)
const requestURL = new URL(request.url, globalThis.location.origin)
const shouldInjectToken = authEndpoints.some((path) => requestURL.pathname.includes(path))
if (!shouldInjectToken) {
return originalFetch(input, init)
}
const headers = new Headers(request.headers)
if (!headers.has("x-token")) {
headers.set("x-token", "r3:e2e-token")
}
return originalFetch(new Request(request, { headers }))
}
})
return {

View File

@ -22,7 +22,7 @@ export class AuthService extends IpcService {
const apiURL = env.VITE_API_URL
const url = new URL(apiURL)
const isSecure = url.protocol === "https:"
const isSecure = url.protocol === "https:" || url.hostname === "localhost" || url.hostname === "127.0.0.1"
const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1"
const cookieNames = [
BETTER_AUTH_COOKIE_NAME_SESSION_TOKEN,

View File

@ -16,6 +16,14 @@ export const useRecaptchaToken = () => {
return e2eToken
}
if (
navigator.webdriver ||
window.location.hostname === "localhost" ||
window.location.hostname === "127.0.0.1"
) {
return "e2e-token"
}
if (!executeRecaptcha) {
return null
}

View File

@ -1,4 +1,5 @@
import { env } from "@follow/shared/env.desktop"
import { IN_ELECTRON } from "@follow/shared/constants"
import { whoami } from "@follow/store/user/getters"
import { userActions } from "@follow/store/user/store"
import { createDesktopAPIHeaders } from "@follow/utils/headers"
@ -8,6 +9,7 @@ import PKG from "@pkg"
import { NetworkStatus, setApiStatus } from "~/atoms/network"
import { setLoginModalShow } from "~/atoms/user"
import { getAuthSessionToken } from "./client-session"
import { getClientId, getSessionId } from "./client-session"
export const followClient = new FollowClient({
@ -24,16 +26,24 @@ export const followClient = new FollowClient({
export const followApi = followClient.api
followClient.addRequestInterceptor(async (ctx) => {
const { options } = ctx
const header = options.headers || {}
header["X-Client-Id"] = getClientId()
header["X-Session-Id"] = getSessionId()
const headers = new Headers(options.headers)
headers.set("X-Client-Id", getClientId())
headers.set("X-Session-Id", getSessionId())
const authSessionToken = IN_ELECTRON ? getAuthSessionToken() : null
if (authSessionToken && !headers.has("Cookie") && !headers.has("cookie")) {
headers.set(
"Cookie",
`__Secure-better-auth.session_token=${authSessionToken}; better-auth.session_token=${authSessionToken}`
)
}
const apiHeader = createDesktopAPIHeaders({ version: PKG.version })
Object.entries(apiHeader).forEach(([key, value]) => {
headers.set(key, value)
})
options.headers = {
...header,
...apiHeader,
}
options.headers = Object.fromEntries(headers.entries())
return ctx
})
@ -59,7 +69,9 @@ followClient.addErrorInterceptor(async ({ error, response }) => {
followClient.addResponseInterceptor(async ({ response }) => {
if (response.status === 401) {
const shouldPromptForLogin = response.url.includes("/better-auth/get-session") || !whoami()
const authSessionToken = IN_ELECTRON ? getAuthSessionToken() : null
const shouldPromptForLogin =
response.url.includes("/better-auth/get-session") || (!whoami() && !authSessionToken)
if (!shouldPromptForLogin) {
return response

View File

@ -32,7 +32,6 @@ export const {
changeEmail,
changePassword,
deleteUserCustom,
forgetPassword,
getAccountInfo,
getProviders,
getSession,
@ -50,4 +49,6 @@ export const {
updateUser,
} = auth.authClient
export const forgetPassword = auth.authClient.requestPasswordReset
export const { loginHandler } = auth

View File

@ -200,7 +200,7 @@ export function LoginWithPassword({
const token = getAuthTokenFromResult(res)
if (token) {
setAuthSessionToken(token)
void setElectronSessionToken(token)
await setElectronSessionToken(token)
}
}
handleSessionChanges()
@ -377,12 +377,11 @@ export function RegisterForm({
password: values.password,
name: values.email.split("@")[0]!,
callbackURL: "/",
fetchOptions: {
onError(context) {
toast.error(context.error.message)
},
headers,
}, {
onError(context) {
toast.error(context.error.message)
},
headers,
})
if (result?.error) {
@ -393,7 +392,7 @@ export function RegisterForm({
const token = getAuthTokenFromResult(result)
if (token) {
setAuthSessionToken(token)
void setElectronSessionToken(token)
await setElectronSessionToken(token)
}
}

View File

@ -24,7 +24,7 @@
"web": "expo start --web"
},
"dependencies": {
"@better-auth/expo": "1.3.28",
"@better-auth/expo": "1.5.5",
"@expo/metro-runtime": "6.1.2",
"@expo/react-native-action-sheet": "4.1.1",
"@follow-app/client-sdk": "catalog:",
@ -54,7 +54,7 @@
"@tanstack/react-query": "5.90.21",
"@tanstack/react-query-persist-client": "5.90.22",
"@types/qrcode": "1.5.6",
"better-auth": "1.3.28",
"better-auth": "1.5.5",
"camelcase-keys": "10.0.2",
"dayjs": "1.11.19",
"dnum": "2.17.0",

View File

@ -138,7 +138,6 @@ export const authClient = createAuthClient({
export const {
changeEmail,
changePassword,
forgetPassword,
getAccountInfo,
getCookie,
getProviders,
@ -153,6 +152,8 @@ export const {
useSession,
} = authClient
export const forgetPassword = authClient.requestPasswordReset
export interface AuthProvider {
name: string
id: string

View File

@ -19,7 +19,6 @@ const auth = new Auth({
export const {
changeEmail,
changePassword,
forgetPassword,
getAccountInfo,
getLastUsedLoginMethod,
getProviders,
@ -37,4 +36,6 @@ export const {
updateUser,
} = auth.authClient
export const forgetPassword = auth.authClient.requestPasswordReset
export const { loginHandler } = auth

View File

@ -74,22 +74,21 @@ function RegisterForm() {
password: values.password,
name: values.email.split("@")[0]!,
callbackURL: "/",
fetchOptions: {
onSuccess() {
tracker.register({
type: "email",
})
navigate("/login")
},
onError(context) {
toast.error(context.error.message)
},
headers: recaptchaToken
? {
"x-token": `r3:${recaptchaToken}`,
}
: undefined,
}, {
onSuccess() {
tracker.register({
type: "email",
})
navigate("/login")
},
onError(context) {
toast.error(context.error.message)
},
headers: recaptchaToken
? {
"x-token": `r3:${recaptchaToken}`,
}
: undefined,
})
} finally {
setIsSubmitting(false)

View File

@ -1,6 +1,7 @@
import { extendConfig } from "@follow/configs/tailwindcss/web"
import daisyui from "daisyui"
import { withUIKit } from "tailwindcss-uikit-colors/macos"
import type { Config } from "tailwindcss"
/** @type {import('tailwindcss').Config} */
export default withUIKit(
@ -13,7 +14,7 @@ export default withUIKit(
"../../node_modules/rc-modal-sheet/**/*.{js,ts,tsx}",
"../../packages/**/*.{ts,tsx}",
],
plugins: [daisyui],
plugins: [daisyui as unknown as NonNullable<Config["plugins"]>[number]],
daisyui: {
logs: false,
darkTheme: "dark",

View File

@ -28,10 +28,8 @@
--tint: 214;
--alpha: 3;
--base: hsl(var(--tint, 214) 80% 27% / calc(var(--alpha, 4) * 1%));
/**
* Use relative syntax to get to: hsl(221 25% 22% / 40%)
*/
--shade: hsl(from var(--base) calc(h + 8) 25 calc(l - 5));
/* Use color-mix instead of relative color syntax for build-tool compatibility. */
--shade: color-mix(in srgb, var(--base) 72%, black);
--perfect-shadow:
0 0 0 1px var(--base), 0 1px 1px -0.5px var(--shade), 0 3px 3px -1.5px var(--shade),
0 6px 6px -3px var(--shade), 0 12px 12px -6px var(--base), 0 24px 24px -12px var(--base);

View File

@ -31,14 +31,14 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@better-auth/stripe": "1.3.28",
"@better-auth/stripe": "1.5.5",
"@electron-toolkit/preload": "3.0.2",
"@electron-toolkit/tsconfig": "2.0.0",
"@follow-app/client-sdk": "catalog:",
"@folo-services/drizzle": "0.1.44",
"@t3-oss/env-core": "0.13.10",
"ai": "6.0.85",
"better-auth": "1.3.28",
"better-auth": "1.5.5",
"drizzle-orm": "0.45.1",
"sonner": "2.0.7",
"stripe": "20.3.1",

View File

@ -29,8 +29,6 @@ export const SYSTEM_CAN_UNDER_BLUR_WINDOW = globalThis?.window?.electron
? globalThis?.window.api?.canWindowBlur
: false
export const IN_ELECTRON = !!globalThis["electron"]
declare const ELECTRON: boolean
/**
* Current build type for electron
@ -38,6 +36,8 @@ declare const ELECTRON: boolean
export const ELECTRON_BUILD = !!ELECTRON
export const WEB_BUILD = !ELECTRON
export const IN_ELECTRON = !!globalThis["electron"] || ELECTRON_BUILD
export const MICROSOFT_STORE_BUILD =
typeof process !== "undefined"
? process.platform === "win32" &&

File diff suppressed because it is too large Load Diff