mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(auth): различать устаревшего бота и ненастроенного на экране логина
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import apiClient from './client';
|
||||
import { isEndpointMissingError } from '../utils/api-error';
|
||||
import type { AnimationConfig } from '@/components/ui/backgrounds/types';
|
||||
import { DEFAULT_ANIMATION_CONFIG } from '@/components/ui/backgrounds/types';
|
||||
|
||||
@@ -36,6 +37,8 @@ export interface TelegramWidgetConfig {
|
||||
request_access: boolean;
|
||||
oidc_enabled: boolean;
|
||||
oidc_client_id: string;
|
||||
/** The bot answered 404 on the config route — build older than v3.24.0. */
|
||||
endpoint_missing?: boolean;
|
||||
}
|
||||
|
||||
export interface OfflineConvGoal {
|
||||
@@ -321,7 +324,7 @@ export const brandingApi = {
|
||||
'/cabinet/branding/telegram-widget',
|
||||
);
|
||||
return response.data;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
return {
|
||||
bot_username: import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '',
|
||||
size: 'large',
|
||||
@@ -330,6 +333,7 @@ export const brandingApi = {
|
||||
request_access: true,
|
||||
oidc_enabled: false,
|
||||
oidc_client_id: '',
|
||||
endpoint_missing: isEndpointMissingError(err),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useAuthStore } from '../store/auth';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { getPendingCampaignSlug } from '../utils/campaign';
|
||||
import { copyToClipboard } from '../utils/clipboard';
|
||||
import { isEndpointMissingError } from '../utils/api-error';
|
||||
|
||||
interface TelegramLoginButtonProps {
|
||||
referralCode?: string;
|
||||
@@ -159,7 +160,6 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
|
||||
isOIDC,
|
||||
widgetConfig?.oidc_client_id,
|
||||
widgetConfig?.request_access,
|
||||
t,
|
||||
scriptLoaded,
|
||||
handleScriptFailed,
|
||||
]);
|
||||
@@ -330,8 +330,9 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
|
||||
},
|
||||
(expires_in || 300) * 1000,
|
||||
);
|
||||
} catch {
|
||||
setDeepLinkError(t('common.error'));
|
||||
} catch (err) {
|
||||
// 404 = the deep-link auth routes don't exist on this bot build (< v3.33.0)
|
||||
setDeepLinkError(t(isEndpointMissingError(err) ? 'auth.botOutdated' : 'common.error'));
|
||||
}
|
||||
}, [botUsername, loginWithDeepLink, navigate, t]);
|
||||
|
||||
@@ -421,9 +422,11 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
|
||||
}, [deepLinkPolling, deepLinkToken, loginWithDeepLink, navigate, t]);
|
||||
|
||||
if (!botUsername || botUsername === 'your_bot') {
|
||||
// 404 on the config route = the bot build predates the cabinet endpoints,
|
||||
// which reads as "not configured" but is actually a version mismatch (#345).
|
||||
return (
|
||||
<div className="py-4 text-center text-sm text-dark-400">
|
||||
{t('auth.telegramNotConfigured')}
|
||||
{t(widgetConfig?.endpoint_missing ? 'auth.botOutdated' : 'auth.telegramNotConfigured')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@
|
||||
"telegramRetryFailed": "Authorization failed. Close the app and try again.",
|
||||
"telegramReopenHint": "If the problem persists, close and reopen the app",
|
||||
"telegramNotConfigured": "Telegram bot is not configured",
|
||||
"botOutdated": "The bot version is not compatible with the cabinet. Update the bot to v3.33.0 or newer.",
|
||||
"telegramUnavailable": "Telegram login is temporarily unavailable",
|
||||
"authenticating": "Authenticating...",
|
||||
"orOpenInApp": "Or open the bot in the app",
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"telegramRetryFailed": "تایید هویت ناموفق بود. برنامه را ببندید و دوباره باز کنید.",
|
||||
"telegramReopenHint": "اگر مشکل ادامه دارد، برنامه را ببندید و دوباره باز کنید",
|
||||
"telegramNotConfigured": "ربات تلگرام پیکربندی نشده",
|
||||
"botOutdated": "نسخهٔ ربات با کابینت سازگار نیست. ربات را به v3.33.0 یا جدیدتر بهروزرسانی کنید.",
|
||||
"telegramUnavailable": "ورود از طریق تلگرام موقتاً در دسترس نیست",
|
||||
"authenticating": "در حال تایید هویت...",
|
||||
"orOpenInApp": "یا ربات را در برنامه باز کنید",
|
||||
|
||||
@@ -192,6 +192,7 @@
|
||||
"telegramRetryFailed": "Авторизация не удалась. Закройте приложение и откройте заново.",
|
||||
"telegramReopenHint": "Если проблема повторяется, закройте и откройте приложение заново",
|
||||
"telegramNotConfigured": "Telegram бот не настроен",
|
||||
"botOutdated": "Версия бота несовместима с кабинетом. Обновите бота до v3.33.0 или новее.",
|
||||
"telegramUnavailable": "Вход через Telegram временно недоступен",
|
||||
"authenticating": "Авторизация...",
|
||||
"orOpenInApp": "Или откройте бота в приложении",
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"telegramRetryFailed": "授权失败。请关闭应用后重新打开。",
|
||||
"telegramReopenHint": "如果问题持续存在,请关闭并重新打开应用",
|
||||
"telegramNotConfigured": "Telegram机器人未配置",
|
||||
"botOutdated": "机器人版本与后台不兼容,请将机器人更新至 v3.33.0 或更高版本。",
|
||||
"telegramUnavailable": "Telegram登录暂时不可用",
|
||||
"authenticating": "正在验证...",
|
||||
"orOpenInApp": "或在应用中打开机器人",
|
||||
|
||||
59
src/utils/api-error.test.ts
Normal file
59
src/utils/api-error.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { AxiosError, AxiosHeaders } from 'axios';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { getApiErrorMessage, isEndpointMissingError } from './api-error';
|
||||
|
||||
function axiosErrorWithStatus(status: number, detail?: unknown): AxiosError {
|
||||
const headers = new AxiosHeaders();
|
||||
const config = { headers };
|
||||
return new AxiosError(
|
||||
'Request failed',
|
||||
'ERR_BAD_REQUEST',
|
||||
config,
|
||||
{},
|
||||
{
|
||||
status,
|
||||
statusText: '',
|
||||
headers,
|
||||
config,
|
||||
data: detail === undefined ? {} : { detail },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
describe('isEndpointMissingError', () => {
|
||||
it('returns true for an axios 404', () => {
|
||||
expect(isEndpointMissingError(axiosErrorWithStatus(404))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for other axios statuses', () => {
|
||||
expect(isEndpointMissingError(axiosErrorWithStatus(403))).toBe(false);
|
||||
expect(isEndpointMissingError(axiosErrorWithStatus(500))).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for non-axios errors', () => {
|
||||
expect(isEndpointMissingError(new Error('boom'))).toBe(false);
|
||||
expect(isEndpointMissingError(undefined)).toBe(false);
|
||||
expect(isEndpointMissingError(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApiErrorMessage', () => {
|
||||
it('returns string detail from an axios error', () => {
|
||||
expect(getApiErrorMessage(axiosErrorWithStatus(400, 'Code must not be empty'), 'fb')).toBe(
|
||||
'Code must not be empty',
|
||||
);
|
||||
});
|
||||
|
||||
it('joins pydantic validation errors', () => {
|
||||
const err = axiosErrorWithStatus(422, [
|
||||
{ loc: ['body', 'name'], msg: 'field required' },
|
||||
{ loc: ['body', 'days'], msg: 'must be positive' },
|
||||
]);
|
||||
expect(getApiErrorMessage(err, 'fb')).toBe('name: field required; days: must be positive');
|
||||
});
|
||||
|
||||
it('falls back for non-axios errors and missing detail', () => {
|
||||
expect(getApiErrorMessage(new Error('boom'), 'fb')).toBe('fb');
|
||||
expect(getApiErrorMessage(axiosErrorWithStatus(500), 'fb')).toBe('fb');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,15 @@
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* True when the backend answered 404 on the route itself — for cabinet
|
||||
* endpoints that means the bot build is too old to have them (e.g. the
|
||||
* deep-link auth routes exist only since bot v3.33.0), as opposed to a
|
||||
* missing entity inside a handler.
|
||||
*/
|
||||
export function isEndpointMissingError(err: unknown): boolean {
|
||||
return axios.isAxiosError(err) && err.response?.status === 404;
|
||||
}
|
||||
|
||||
export function getApiErrorMessage(err: unknown, fallback: string): string {
|
||||
if (axios.isAxiosError(err)) {
|
||||
const detail = err.response?.data?.detail;
|
||||
|
||||
Reference in New Issue
Block a user