Files
cstb-next/docs/web-integration-example.md
2025-11-05 11:21:13 +08:00

7.1 KiB

网页端集成示例代码

本文档提供网页端(https://cstb.upup.cool/)实现登录/注册回调的示例代码

登录页面示例

auth/login 页面中:

'use client'

import { useState } from 'react'
import { createClient } from '@/utils/supabase/client'
import { useSearchParams } from 'next/navigation'

export default function LoginPage() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [loading, setLoading] = useState(false)
  const searchParams = useSearchParams()
  const redirectTo = searchParams.get('redirect') // 应该是 'cstb://auth'

  const supabase = createClient()

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)

    try {
      const { data, error } = await supabase.auth.signInWithPassword({
        email,
        password,
      })

      if (error) {
        alert(`登录失败: ${error.message}`)
        setLoading(false)
        return
      }

      if (data.session) {
        // 如果有 redirect 参数,说明是从应用跳转过来的
        if (redirectTo) {
          // 构建 deep-link URL
          const redirectUrl = new URL(redirectTo)
          redirectUrl.searchParams.set('access_token', data.session.access_token)
          redirectUrl.searchParams.set('refresh_token', data.session.refresh_token)
          
          // 重定向到应用
          window.location.href = redirectUrl.toString()
        } else {
          // 正常网页端跳转
          window.location.href = '/'
        }
      }
    } catch (error) {
      console.error('Login error:', error)
      alert('登录时发生错误')
      setLoading(false)
    }
  }

  return (
    <form onSubmit={handleLogin}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? '登录中...' : '登录'}
      </button>
    </form>
  )
}

注册页面示例

auth/signup 页面中:

'use client'

import { useState } from 'react'
import { createClient } from '@/utils/supabase/client'
import { useSearchParams } from 'next/navigation'

export default function SignupPage() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [confirmPassword, setConfirmPassword] = useState('')
  const [loading, setLoading] = useState(false)
  const searchParams = useSearchParams()
  const redirectTo = searchParams.get('redirect') // 应该是 'cstb://auth'

  const supabase = createClient()

  const handleSignup = async (e: React.FormEvent) => {
    e.preventDefault()
    
    if (password !== confirmPassword) {
      alert('两次输入的密码不一致')
      return
    }

    setLoading(true)

    try {
      const { data, error } = await supabase.auth.signUp({
        email,
        password,
      })

      if (error) {
        alert(`注册失败: ${error.message}`)
        setLoading(false)
        return
      }

      if (data.session) {
        // 如果有 redirect 参数,说明是从应用跳转过来的
        if (redirectTo) {
          // 构建 deep-link URL
          const redirectUrl = new URL(redirectTo)
          redirectUrl.searchParams.set('access_token', data.session.access_token)
          redirectUrl.searchParams.set('refresh_token', data.session.refresh_token)
          
          // 重定向到应用
          window.location.href = redirectUrl.toString()
        } else {
          // 正常网页端跳转
          alert('注册成功!请检查邮箱验证链接。')
          window.location.href = '/'
        }
      } else {
        // 需要邮箱验证
        alert('注册成功!请检查邮箱中的验证链接。')
        if (!redirectTo) {
          window.location.href = '/'
        }
      }
    } catch (error) {
      console.error('Signup error:', error)
      alert('注册时发生错误')
      setLoading(false)
    }
  }

  return (
    <form onSubmit={handleSignup}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
        required
      />
      <input
        type="password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
        placeholder="确认密码"
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? '注册中...' : '注册'}
      </button>
    </form>
  )
}

使用 OAuth 提供商的示例

如果使用第三方登录(如 Google、GitHub 等):

const handleOAuthLogin = async (provider: 'google' | 'github') => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider,
    options: {
      redirectTo: redirectTo || `${window.location.origin}/auth/callback`,
    },
  })

  if (error) {
    alert(`登录失败: ${error.message}`)
    return
  }

  // OAuth 会重定向到回调页面,在回调页面中处理
}

回调页面处理

如果需要处理 OAuth 回调:

// auth/callback/page.tsx
'use client'

import { useEffect } from 'react'
import { createClient } from '@/utils/supabase/client'
import { useSearchParams } from 'next/navigation'

export default function CallbackPage() {
  const searchParams = useSearchParams()
  const redirectTo = searchParams.get('redirect') // 从应用跳转时的原始 redirect

  useEffect(() => {
    const handleCallback = async () => {
      const supabase = createClient()
      const { data: { session }, error } = await supabase.auth.getSession()

      if (error) {
        console.error('Error getting session:', error)
        return
      }

      if (session) {
        // 如果是从应用跳转过来的,重定向回应用
        if (redirectTo) {
          const redirectUrl = new URL(redirectTo)
          redirectUrl.searchParams.set('access_token', session.access_token)
          redirectUrl.searchParams.set('refresh_token', session.refresh_token)
          window.location.href = redirectUrl.toString()
        } else {
          // 正常网页端跳转
          window.location.href = '/'
        }
      }
    }

    void handleCallback()
  }, [redirectTo])

  return <div>正在处理登录...</div>
}

注意事项

  1. 安全性: 确保只在 HTTPS 环境下使用,避免 token 泄露
  2. 错误处理: 始终处理登录/注册失败的情况
  3. 用户体验: 在重定向前显示加载状态
  4. 验证: 如果启用了邮箱验证,需要处理未验证的情况

测试步骤

  1. 在应用中使用 https://cstb.upup.cool/auth/login?redirect=cstb://auth 打开登录页面
  2. 完成登录后,应该自动跳转回应用
  3. 检查应用是否成功接收并设置了 session