perf(vpn-bot): фоновая подготовка подписки, оптимизация запросов, анимация кнопок
- validateTelemostRoom: таймаут 3 сек + кэш 10 мин - createVpnTask: транзакция + batch insert field values - prepareVpnSubscription: валидация и создание подписки в фоне при получении ссылки - sendVpnBotMessage: кэш botId, Promise.all для enrich/members/orgMap - processPlatformSelection: одно сообщение с файлом и подпиской вместо двух - Chat.tsx: нажатая кнопка становится зелёной галочкой, остальные disabled
This commit is contained in:
@@ -72,7 +72,14 @@ export function extractRoomId(roomUrl: string): string {
|
||||
return roomUrl.slice(idx + 3);
|
||||
}
|
||||
|
||||
const telemostValidationCache = new Map<string, { valid: boolean; expiresAt: number }>();
|
||||
|
||||
export async function validateTelemostRoom(roomUrl: string): Promise<boolean> {
|
||||
const cached = telemostValidationCache.get(roomUrl);
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.valid;
|
||||
}
|
||||
|
||||
try {
|
||||
const encoded = encodeURIComponent(roomUrl);
|
||||
const url = `${VPN_TELEMOST_API_BASE}/conferences/${encoded}/connection?next_gen_media_platform_allowed=true&display_name=VPN-Bot&waiting_room_supported=true`;
|
||||
@@ -87,20 +94,49 @@ export async function validateTelemostRoom(roomUrl: string): Promise<boolean> {
|
||||
Origin: 'https://telemost.yandex.ru',
|
||||
Referer: 'https://telemost.yandex.ru/',
|
||||
},
|
||||
signal: AbortSignal.timeout(3000),
|
||||
});
|
||||
return resp.status === 200;
|
||||
const valid = resp.status === 200;
|
||||
telemostValidationCache.set(roomUrl, { valid, expiresAt: Date.now() + 10 * 60 * 1000 });
|
||||
return valid;
|
||||
} catch (err) {
|
||||
console.error('[VPN] Telemost validation error:', err);
|
||||
telemostValidationCache.set(roomUrl, { valid: false, expiresAt: Date.now() + 60 * 1000 });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getOrCreateVpnSubscription(
|
||||
export interface PreparedSubscription {
|
||||
subscriptionUrl: string;
|
||||
isNew: boolean;
|
||||
taskId?: number;
|
||||
roomId: string;
|
||||
cryptoKey?: string;
|
||||
}
|
||||
|
||||
export async function getExistingVpnSubscription(
|
||||
organizationId: number,
|
||||
roomUrl: string,
|
||||
): Promise<PreparedSubscription | null> {
|
||||
const existing = await findVpnTaskByRoomUrl(roomUrl, organizationId);
|
||||
if (!existing) return null;
|
||||
const token = existing.values.subscription_token as string | undefined;
|
||||
if (!token) return null;
|
||||
return {
|
||||
subscriptionUrl: buildSubscriptionUrl(token),
|
||||
isNew: false,
|
||||
taskId: existing.task.id,
|
||||
roomId: extractRoomId(roomUrl),
|
||||
cryptoKey: existing.values.crypto_key as string | undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function prepareVpnSubscription(
|
||||
organizationId: number,
|
||||
userId: number,
|
||||
userName: string,
|
||||
roomUrl: string,
|
||||
): Promise<{ subscriptionUrl: string; isNew: boolean }> {
|
||||
): Promise<PreparedSubscription> {
|
||||
const existing = await findVpnTaskByRoomUrl(roomUrl, organizationId);
|
||||
if (existing && existing.task.createdBy !== userId) {
|
||||
throw new VpnError('Эта комната уже используется другим сотрудником', 'ROOM_IN_USE');
|
||||
@@ -108,7 +144,13 @@ export async function getOrCreateVpnSubscription(
|
||||
if (existing) {
|
||||
const token = existing.values.subscription_token as string | undefined;
|
||||
if (!token) throw new VpnError('Подписка повреждена, обратитесь к администратору', 'BROKEN');
|
||||
return { subscriptionUrl: buildSubscriptionUrl(token), isNew: false };
|
||||
return {
|
||||
subscriptionUrl: buildSubscriptionUrl(token),
|
||||
isNew: false,
|
||||
taskId: existing.task.id,
|
||||
roomId: extractRoomId(roomUrl),
|
||||
cryptoKey: existing.values.crypto_key as string | undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const roomId = extractRoomId(roomUrl);
|
||||
@@ -125,7 +167,7 @@ export async function getOrCreateVpnSubscription(
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setDate(expiresAt.getDate() + DEFAULT_SUBSCRIPTION_DAYS);
|
||||
|
||||
await createVpnTask({
|
||||
const task = await createVpnTask({
|
||||
organizationId,
|
||||
createdBy: userId,
|
||||
title: `VPN ${userName || `#${userId}`} / ${roomId}`,
|
||||
@@ -144,7 +186,24 @@ export async function getOrCreateVpnSubscription(
|
||||
},
|
||||
});
|
||||
|
||||
return { subscriptionUrl: buildSubscriptionUrl(token), isNew: true };
|
||||
return {
|
||||
subscriptionUrl: buildSubscriptionUrl(token),
|
||||
isNew: true,
|
||||
taskId: task.id,
|
||||
roomId,
|
||||
cryptoKey,
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated Use prepareVpnSubscription instead */
|
||||
export async function getOrCreateVpnSubscription(
|
||||
organizationId: number,
|
||||
userId: number,
|
||||
userName: string,
|
||||
roomUrl: string,
|
||||
): Promise<{ subscriptionUrl: string; isNew: boolean }> {
|
||||
const result = await prepareVpnSubscription(organizationId, userId, userName, roomUrl);
|
||||
return { subscriptionUrl: result.subscriptionUrl, isNew: result.isNew };
|
||||
}
|
||||
|
||||
export async function fetchSubscription(token: string, hwid: string | undefined): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user