418 lines
9.4 KiB
Plaintext
418 lines
9.4 KiB
Plaintext
<template>
|
|
<view class="container">
|
|
<view class="header">
|
|
<text class="title">Supabase 连接测试</text>
|
|
</view>
|
|
|
|
<view class="config-info">
|
|
<text class="label">当前配置:</text>
|
|
<text class="value">URL: {{ config.url }}</text>
|
|
<text class="value">Key: {{ config.keyMasked }}</text>
|
|
</view>
|
|
|
|
<view class="test-section">
|
|
<button class="btn-primary" @click="runAllTests" :disabled="isLoading">
|
|
{{ isLoading ? '测试中...' : '运行全部测试' }}
|
|
</button>
|
|
</view>
|
|
|
|
<view class="results" v-if="testResults.length > 0">
|
|
<text class="section-title">测试结果</text>
|
|
<view
|
|
class="result-item"
|
|
v-for="(result, index) in testResults"
|
|
:key="index"
|
|
:class="{ success: result.success, error: !result.success }"
|
|
>
|
|
<text class="result-icon">{{ result.success ? '✅' : '❌' }}</text>
|
|
<text class="result-text">{{ result.name }}</text>
|
|
<text class="result-message">{{ result.message }}</text>
|
|
<text v-if="result.errorDetails" class="error-details">{{ result.errorDetails }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="quick-tests">
|
|
<text class="section-title">单项测试</text>
|
|
<button class="btn-secondary" @click="testConnection">测试连接</button>
|
|
<button class="btn-secondary" @click="testQuery">测试查询</button>
|
|
<button class="btn-secondary" @click="testAuth">测试认证</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="uts">
|
|
// 导入你的封装库和配置
|
|
import supa from '@/components/supadb/aksupainstance.uts'
|
|
import { SUPA_URL, SUPA_KEY } from '@/ak/config.uts'
|
|
|
|
// 定义测试结果类型
|
|
type TestResult = {
|
|
name: string
|
|
success: boolean
|
|
message: string
|
|
errorDetails?: string
|
|
}
|
|
|
|
// 定义用户类型(用于类型安全查询)
|
|
type User = {
|
|
id?: number
|
|
email?: string
|
|
nickname?: string
|
|
phone?: string
|
|
created_at?: string
|
|
}
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
config: {
|
|
url: SUPA_URL,
|
|
key: SUPA_KEY,
|
|
keyMasked: SUPA_KEY.substring(0, 20) + '...' // 隐藏部分密钥
|
|
},
|
|
isLoading: false,
|
|
testResults: [] as TestResult[]
|
|
}
|
|
},
|
|
onLoad() {
|
|
console.log('🎯 Supabase 测试页面加载完成')
|
|
console.log('📋 配置信息:', {
|
|
url: SUPA_URL,
|
|
hasKey: !!SUPA_KEY
|
|
})
|
|
},
|
|
methods: {
|
|
// 获取错误消息的安全函数
|
|
getErrorMessage(err: any): string {
|
|
if (err == null) return '未知错误'
|
|
if (typeof err === 'string') return err
|
|
if (typeof err === 'object') {
|
|
if (err.message) return err.message
|
|
if (err.error) return err.error
|
|
if (err.msg) return err.msg
|
|
return JSON.stringify(err)
|
|
}
|
|
return String(err)
|
|
},
|
|
|
|
// 安全执行测试
|
|
async safeExecute<T>(promise: Promise<T>, operationName: string): Promise<{ success: boolean; data?: T; error?: string }> {
|
|
try {
|
|
const result = await promise
|
|
return { success: true, data: result }
|
|
} catch (err) {
|
|
const errorMessage = this.getErrorMessage(err)
|
|
console.error(`❌ ${operationName} 失败:`, err)
|
|
return { success: false, error: errorMessage }
|
|
}
|
|
},
|
|
|
|
// 测试连接
|
|
async testConnection(): Promise<TestResult> {
|
|
console.log('🔍 开始测试连接...')
|
|
|
|
const result = await this.safeExecute(
|
|
uni.request({
|
|
url: `${SUPA_URL}/rest/v1/`,
|
|
method: 'GET',
|
|
header: {
|
|
'apikey': SUPA_KEY,
|
|
'Authorization': `Bearer ${SUPA_KEY}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
timeout: 10000
|
|
}),
|
|
'基础连接测试'
|
|
)
|
|
|
|
if (result.success) {
|
|
const response = result.data as any
|
|
if (response.statusCode === 200 || response.statusCode === 404) {
|
|
return {
|
|
name: '基础连接',
|
|
success: true,
|
|
message: `API 可达 (HTTP ${response.statusCode})`,
|
|
errorDetails: response.statusCode === 404 ? 'API 响应正常,但请求的资源不存在' : undefined
|
|
}
|
|
} else {
|
|
return {
|
|
name: '基础连接',
|
|
success: false,
|
|
message: `HTTP 错误`,
|
|
errorDetails: `状态码: ${response.statusCode}, 数据: ${JSON.stringify(response.data)}`
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
name: '基础连接',
|
|
success: false,
|
|
message: '连接失败',
|
|
errorDetails: result.error
|
|
}
|
|
}
|
|
},
|
|
|
|
// 测试查询
|
|
async testQuery(): Promise<TestResult> {
|
|
console.log('🔍 开始测试查询...')
|
|
|
|
// 尝试查询 users 表
|
|
const queryResult = await this.safeExecute(
|
|
supa.from('users').select('id, email, nickname').limit(1).execute(),
|
|
'查询测试'
|
|
)
|
|
|
|
if (queryResult.success) {
|
|
const { data, error } = queryResult.data as any
|
|
if (error) {
|
|
const errorMsg = this.getErrorMessage(error)
|
|
// 检查是否是表不存在的错误
|
|
if (errorMsg.includes('relation') && errorMsg.includes('does not exist')) {
|
|
return {
|
|
name: '查询测试',
|
|
success: false,
|
|
message: '表不存在',
|
|
errorDetails: 'users 表不存在,建议先在 Studio 中创建表'
|
|
}
|
|
}
|
|
return {
|
|
name: '查询测试',
|
|
success: false,
|
|
message: '查询出错',
|
|
errorDetails: errorMsg
|
|
}
|
|
}
|
|
return {
|
|
name: '查询测试',
|
|
success: true,
|
|
message: `查询成功,返回 ${data?.length || 0} 条记录`,
|
|
errorDetails: data && data.length > 0 ? `第一条: ${JSON.stringify(data[0])}` : '表为空'
|
|
}
|
|
} else {
|
|
return {
|
|
name: '查询测试',
|
|
success: false,
|
|
message: '查询失败',
|
|
errorDetails: queryResult.error
|
|
}
|
|
}
|
|
},
|
|
|
|
// 测试认证
|
|
async testAuth(): Promise<TestResult> {
|
|
console.log('🔍 开始测试认证...')
|
|
|
|
const authResult = await this.safeExecute(
|
|
supa.auth.getSession(),
|
|
'认证测试'
|
|
)
|
|
|
|
if (authResult.success) {
|
|
const { data, error } = authResult.data as any
|
|
if (error) {
|
|
return {
|
|
name: '认证测试',
|
|
success: false,
|
|
message: '获取会话失败',
|
|
errorDetails: this.getErrorMessage(error)
|
|
}
|
|
}
|
|
if (data?.session) {
|
|
return {
|
|
name: '认证测试',
|
|
success: true,
|
|
message: '用户已登录',
|
|
errorDetails: `用户: ${data.session.user.email || data.session.user.phone || 'ID: ' + data.session.user.id}`
|
|
}
|
|
} else {
|
|
return {
|
|
name: '认证测试',
|
|
success: true,
|
|
message: '未登录(正常状态)',
|
|
errorDetails: '当前没有活动会话,需要先登录'
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
name: '认证测试',
|
|
success: false,
|
|
message: '认证失败',
|
|
errorDetails: authResult.error
|
|
}
|
|
}
|
|
},
|
|
|
|
// 运行所有测试
|
|
async runAllTests() {
|
|
this.isLoading = true
|
|
this.testResults = []
|
|
|
|
try {
|
|
// 依次运行测试
|
|
const tests = [
|
|
{ name: 'connection', func: this.testConnection },
|
|
{ name: 'query', func: this.testQuery },
|
|
{ name: 'auth', func: this.testAuth }
|
|
]
|
|
|
|
for (const test of tests) {
|
|
console.log(`🧪 运行测试: ${test.name}`)
|
|
const result = await test.func.call(this)
|
|
this.testResults.push(result)
|
|
|
|
// 短暂延迟,让用户看到测试进度
|
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
}
|
|
|
|
// 总结结果
|
|
const passed = this.testResults.filter(r => r.success).length
|
|
const total = this.testResults.length
|
|
console.log(`📈 测试总结: ${passed}/${total} 通过`)
|
|
|
|
} catch (error) {
|
|
console.error('❌ 运行测试时发生错误:', error)
|
|
this.testResults.push({
|
|
name: '整体测试',
|
|
success: false,
|
|
message: '测试框架错误',
|
|
errorDetails: this.getErrorMessage(error)
|
|
})
|
|
} finally {
|
|
this.isLoading = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.container {
|
|
padding: 30rpx;
|
|
background-color: #f8f9fa;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.config-info {
|
|
background-color: #fff;
|
|
padding: 20rpx;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.label {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
display: block;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.value {
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.test-section {
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.btn-primary {
|
|
width: 100%;
|
|
height: 80rpx;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: #fff;
|
|
border-radius: 12rpx;
|
|
font-size: 28rpx;
|
|
border: none;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.btn-secondary {
|
|
width: 100%;
|
|
height: 70rpx;
|
|
background-color: #6c757d;
|
|
color: #fff;
|
|
border-radius: 8rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.btn-primary:disabled, .btn-secondary:disabled {
|
|
background-color: #ccc;
|
|
}
|
|
|
|
.results {
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
display: block;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.result-item {
|
|
background-color: #fff;
|
|
padding: 20rpx;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.result-item.success {
|
|
border-left: 4px solid #28a745;
|
|
background-color: #d4edda;
|
|
}
|
|
|
|
.result-item.error {
|
|
border-left: 4px solid #dc3545;
|
|
background-color: #f8d7da;
|
|
}
|
|
|
|
.result-icon {
|
|
font-size: 32rpx;
|
|
margin-right: 10rpx;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.result-text {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.result-message {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
margin-top: 8rpx;
|
|
}
|
|
|
|
.error-details {
|
|
display: block;
|
|
font-size: 20rpx;
|
|
color: #856404;
|
|
background-color: #fff3cd;
|
|
padding: 8rpx;
|
|
border-radius: 4rpx;
|
|
margin-top: 8rpx;
|
|
word-break: break-all;
|
|
white-space: pre-wrap;
|
|
}
|
|
</style> |