使用 Better Auth 的 updateUser API 更新用户信息,使用 SDK 的 FileController 上传头像。 ## 新增功能 - 创建 useUpdateProfile hook 处理用户信息更新 - 支持选择图片上传头像(使用 expo-image-picker) - 先上传头像到 S3,再更新用户信息 ## 更新文件 - lib/auth.ts: 导出 updateUser 方法 - hooks/use-update-profile.ts: 新建更新资料 hook - hooks/index.ts: 导出 useUpdateProfile - components/drawer/EditProfileDrawer.tsx: - 添加头像选择功能(点击相机按钮) - 调用 updateProfile API 保存更改 - 添加加载状态和禁用状态 - 更新 onSave 回调参数类型 - app/(tabs)/my.tsx: - 传递 initialAvatar 给编辑抽屉 - 显示用户真实头像(如有) - 更新 onSave 回调处理 ## 功能流程 1. 点击相机按钮选择图片 2. 调用 uploadFile 上传到 S3 3. 调用 updateUser 更新用户信息 4. 保存成功后刷新 session Co-Authored-By: Claude <noreply@anthropic.com>
129 lines
3.5 KiB
TypeScript
129 lines
3.5 KiB
TypeScript
import 'reflect-metadata'
|
||
|
||
import { expoClient } from '@better-auth/expo/client'
|
||
import { createSkerClientPlugin } from '@repo/sdk'
|
||
import {
|
||
adminClient,
|
||
emailOTPClient,
|
||
genericOAuthClient,
|
||
jwtClient,
|
||
organizationClient,
|
||
phoneNumberClient,
|
||
usernameClient,
|
||
} from 'better-auth/client/plugins'
|
||
import { createAuthClient } from 'better-auth/react'
|
||
|
||
import { fetchWithLogger, TOKEN_KEY } from './fetch-logger'
|
||
import type { Subscription } from './plugins/stripe'
|
||
import { stripeClient } from './plugins/stripe-plugin'
|
||
import { storage } from './storage'
|
||
import type { ApiError } from './types'
|
||
|
||
// 商户ID配置 - 从环境变量或应用配置中读取
|
||
export const OWNER_ID = 't0m9cketSQdCA6cHXI9mXQLJPM9LDIw5'
|
||
|
||
export interface CreditBalance {
|
||
tokenUsage: number
|
||
remainingTokenBalance: number
|
||
currentCreditBalance: number
|
||
unitPrice: number
|
||
}
|
||
|
||
export interface SubscriptionListItem extends Subscription {
|
||
type?: 'metered' | 'licenced'
|
||
limits?: Record<string, number>
|
||
creditBalance?: CreditBalance
|
||
highestTierPlan?: string
|
||
highestTierCredits?: number
|
||
}
|
||
|
||
export interface SubscriptionListParams {
|
||
query?: {
|
||
referenceId?: string
|
||
}
|
||
}
|
||
|
||
export interface SubscriptionRestoreParams {
|
||
referenceId?: string
|
||
subscriptionId?: string
|
||
}
|
||
|
||
export interface SubscriptionRestoreResponse {
|
||
success: boolean
|
||
subscription: {
|
||
id: string
|
||
stripeSubscriptionId: string
|
||
cancelAtPeriodEnd: boolean
|
||
periodEnd?: Date
|
||
status: string
|
||
}
|
||
}
|
||
|
||
export interface BillingPortalParams {
|
||
locale?: string
|
||
referenceId?: string
|
||
returnUrl?: string
|
||
}
|
||
|
||
export interface BillingPortalResponse {
|
||
url: string
|
||
redirect: boolean
|
||
}
|
||
|
||
export interface ISubscription {
|
||
list: (params?: SubscriptionListParams) => Promise<{ data?: SubscriptionListItem[]; error?: ApiError }>
|
||
restore: (params?: SubscriptionRestoreParams) => Promise<{ data?: SubscriptionRestoreResponse; error?: ApiError }>
|
||
billingPortal: (params?: BillingPortalParams) => Promise<{ data?: BillingPortalResponse; error?: ApiError }>
|
||
}
|
||
// 统一的 Token 存储键名(与 fetch-logger.ts 保持一致)
|
||
export const getAuthToken = async () => (await storage.getItem(TOKEN_KEY)) || ''
|
||
|
||
export const setAuthToken = async (token: string) => {
|
||
await storage.setItem(TOKEN_KEY, token)
|
||
}
|
||
export const authClient = createAuthClient({
|
||
baseURL: 'https://api.mixvideo.bowong.cc',
|
||
trustedOrigins: ['duooomi://', 'https://api.mixvideo.bowong.cc'],
|
||
storage,
|
||
scheme: 'duooomi',
|
||
fetchOptions: {
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
// x-ownerid: 商户ID(如果后端需要)
|
||
// 如果 MERCHANT_ID 为空,则不添加此 header
|
||
...(OWNER_ID && { 'x-ownerid': OWNER_ID }),
|
||
},
|
||
auth: {
|
||
type: 'Bearer',
|
||
token: async () => {
|
||
const Authorization = await getAuthToken()
|
||
return Authorization
|
||
},
|
||
},
|
||
customFetchImpl: fetchWithLogger as typeof fetch,
|
||
},
|
||
plugins: [
|
||
createSkerClientPlugin(),
|
||
stripeClient(),
|
||
usernameClient(),
|
||
phoneNumberClient(),
|
||
emailOTPClient(),
|
||
genericOAuthClient(),
|
||
jwtClient(),
|
||
adminClient(),
|
||
organizationClient(),
|
||
expoClient({
|
||
storage: storage as any,
|
||
}),
|
||
],
|
||
})
|
||
|
||
export const { signIn, signUp, signOut, useSession, $Infer, admin, forgetPassword, resetPassword, emailOtp, changePassword, updateUser } =
|
||
authClient
|
||
|
||
// 导出 loomart API(来自 createSkerClientPlugin)
|
||
export const loomart = Reflect.get(authClient, 'loomart')
|
||
|
||
// 导出 subscription API
|
||
export const subscription: ISubscription = Reflect.get(authClient, 'subscription')
|