后端迁移到fastapi (#3)

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-07-18 20:49:09 +08:00
parent 217b81c506
commit 51743645e2
71 changed files with 845 additions and 3580 deletions
+24 -38
View File
@@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import { NAlert, NButton, NCard, NForm, NFormItem, NInput, NResult, NSpin } from 'naive-ui'
import { resolveEmailAuthCallback } from '@/lib/authEmail'
import { supabase } from '@/lib/supabase'
import { apiRequest } from '@/lib/api'
const route = useRoute()
const router = useRouter()
const password = ref('')
@@ -13,10 +13,11 @@ const error = ref('')
const state = ref<'checking' | 'ready' | 'invalid' | 'success'>('checking')
const loading = ref(false)
const countdown = ref(5)
const resetToken = ref('')
let countdownTimer: ReturnType<typeof setInterval> | null = null
const canSubmit = computed(() =>
state.value === 'ready' && password.value.length >= 6 && password.value === confirmPassword.value,
state.value === 'ready' && password.value.length >= 8 && password.value === confirmPassword.value,
)
function showInvalidRecoveryLink() {
@@ -26,19 +27,12 @@ function showInvalidRecoveryLink() {
function getResetErrorMessage(value: unknown) {
const message = value instanceof Error ? value.message : ''
if (
message.includes('Auth session missing')
|| message.includes('session_not_found')
|| message.includes('refresh_token_not_found')
|| message.includes('Invalid Refresh Token')
|| message.includes('otp_expired')
|| message.includes('expired')
) {
if (message.includes('无效') || message.includes('过期') || message.includes('expired')) {
return '密码重置链接无效或已过期,请重新发送邮件。'
}
if (message.includes('Password should be at least')) {
return '新密码至少需要 6 位。'
if (message.includes('8')) {
return '新密码至少需要 8 位。'
}
return message || '密码重置失败,请稍后重试。'
@@ -49,31 +43,27 @@ function startCountdown() {
countdown.value--
if (countdown.value <= 0) {
if (countdownTimer) clearInterval(countdownTimer)
router.push('/')
router.push('/login')
}
}, 1000)
}
async function initializeRecoverySession() {
const result = await resolveEmailAuthCallback({
client: supabase,
allowedTypes: ['recovery'],
})
if (!result.ok || !result.session?.user) {
error.value = getResetErrorMessage(result.ok ? '链接无效或已过期。' : result.error)
state.value = 'invalid'
const token = typeof route.query.token === 'string' ? route.query.token : ''
if (!token) {
showInvalidRecoveryLink()
return
}
resetToken.value = token
state.value = 'ready'
}
async function handleResetPassword() {
error.value = ''
if (password.value.length < 6) {
error.value = '新密码至少需要 6 位。'
if (password.value.length < 8) {
error.value = '新密码至少需要 8 位。'
return
}
if (password.value !== confirmPassword.value) {
@@ -81,17 +71,13 @@ async function handleResetPassword() {
return
}
const { data: { session }, error: sessionError } = await supabase.auth.getSession()
if (sessionError || !session?.user) {
showInvalidRecoveryLink()
return
}
loading.value = true
try {
const { error: updateError } = await supabase.auth.updateUser({ password: password.value })
if (updateError) throw updateError
await apiRequest('/auth/reset-password', {
method: 'POST',
auth: false,
body: { token: resetToken.value, password: password.value },
})
window.history.replaceState(null, '', window.location.pathname)
countdown.value = 5
state.value = 'success'
@@ -123,12 +109,12 @@ onUnmounted(() => {
v-if="state === 'success'"
status="success"
title="密码已重置"
description="你已使用新密码完成更新,正在返回地图页。"
description="你已使用新密码完成更新,现在可以重新登录。"
>
<template #footer>
<div class="space-y-4">
<p class="text-sm text-slate-500">{{ countdown }} 秒后自动跳转地图页面...</p>
<NButton type="primary" class="oc-primary-button oc-primary-button--teal" @click="router.push('/')">进入地图</NButton>
<p class="text-sm text-slate-500">{{ countdown }} 秒后自动跳转登录页面...</p>
<NButton type="primary" class="oc-primary-button oc-primary-button--teal" @click="router.push('/login')">立即登录</NButton>
</div>
</template>
</NResult>
@@ -162,7 +148,7 @@ onUnmounted(() => {
required
show-password-on="click"
autocomplete="new-password"
placeholder="至少 6 位"
placeholder="至少 8 位"
/>
</NFormItem>