4.27.2: Улучшение отладки OAuth Fitbit
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m24s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
poignatov
2026-02-06 21:15:08 +03:00
parent af2aaa4168
commit d355928aa9
4 changed files with 66 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "play-life-web",
"version": "4.27.1",
"version": "4.27.2",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -10,6 +10,7 @@ function FitbitIntegration({ onNavigate }) {
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [message, setMessage] = useState('')
const [oauthError, setOauthError] = useState('')
const [toastMessage, setToastMessage] = useState(null)
const [isLoadingError, setIsLoadingError] = useState(false)
const [goals, setGoals] = useState({
@@ -26,23 +27,35 @@ function FitbitIntegration({ onNavigate }) {
const [editedGoals, setEditedGoals] = useState(goals)
const [syncing, setSyncing] = useState(false)
// Сохраняем OAuth статус из URL в ref, чтобы проверить после checkStatus
const oauthStatusRef = React.useRef(null)
useEffect(() => {
checkStatus()
// Проверяем URL параметры для сообщений
// Проверяем URL параметры для сообщений ДО вызова checkStatus
const params = new URLSearchParams(window.location.search)
const integration = params.get('integration')
const status = params.get('status')
if (integration === 'fitbit') {
oauthStatusRef.current = status
if (status === 'connected') {
setMessage('Fitbit успешно подключен!')
// Очищаем URL параметры
window.history.replaceState({}, '', window.location.pathname)
setMessage('Fitbit успешно подключен!')
} else if (status === 'error') {
const errorMsg = params.get('message') || 'Произошла ошибка'
setToastMessage({ text: errorMsg, type: 'error' })
window.history.replaceState({}, '', window.location.pathname)
const errorMsg = params.get('message') || 'unknown_error'
const errorMessages = {
'config_error': 'Ошибка конфигурации сервера. Обратитесь к администратору.',
'invalid_state': 'Недействительный токен авторизации. Попробуйте ещё раз.',
'no_code': 'Не получен код авторизации от Fitbit. Попробуйте ещё раз.',
'token_exchange_failed': 'Не удалось обменять код на токен. Проверьте настройки Fitbit приложения.',
'user_info_failed': 'Не удалось получить информацию о пользователе Fitbit.',
'db_error': 'Ошибка сохранения данных. Попробуйте ещё раз.',
'unknown_error': 'Произошла неизвестная ошибка при подключении Fitbit.'
}
setOauthError(errorMessages[errorMsg] || `Ошибка: ${errorMsg}`)
}
// Очищаем URL параметры
window.history.replaceState({}, '', window.location.pathname)
}
checkStatus()
}, [])
useEffect(() => {
@@ -65,6 +78,12 @@ function FitbitIntegration({ onNavigate }) {
setGoals(data.goals)
setEditedGoals(data.goals)
}
// Если OAuth вернул status=connected, но бэкенд не подтвердил подключение
if (oauthStatusRef.current === 'connected' && !data.connected) {
setOauthError('Авторизация в Fitbit прошла, но подключение не сохранилось. Попробуйте ещё раз или обратитесь к администратору.')
setMessage('')
}
oauthStatusRef.current = null
} catch (error) {
console.error('Error checking status:', error)
setError(error.message || 'Не удалось проверить статус')
@@ -250,6 +269,12 @@ function FitbitIntegration({ onNavigate }) {
<p className="text-green-800">{message}</p>
</div>
)}
{oauthError && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<p className="text-red-800">{oauthError}</p>
<button onClick={() => setOauthError('')} className="text-red-600 text-sm underline mt-1">Скрыть</button>
</div>
)}
{/* Статистика */}
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
@@ -440,6 +465,13 @@ function FitbitIntegration({ onNavigate }) {
</div>
) : (
<div>
{oauthError && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<p className="text-red-800 font-medium">Ошибка подключения Fitbit</p>
<p className="text-red-700 mt-1">{oauthError}</p>
<button onClick={() => setOauthError('')} className="text-red-600 text-sm underline mt-2">Скрыть</button>
</div>
)}
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Подключение Fitbit</h2>
<p className="text-gray-700 mb-4">