📡 API: Получение информации о подписке пользователя
This commit is contained in:
128
app/api/user-subscription/route.ts
Normal file
128
app/api/user-subscription/route.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { MARZBAN_PANEL_URL, getSubscriptionUrl } from '@/lib/constants';
|
||||
import { logger } from '@/lib/logger';
|
||||
import type { MarzbanUser } from '@/types/marzban';
|
||||
|
||||
// Next.js 13+ caching: no-store для реального времени данных подписки
|
||||
export const dynamic = 'force-dynamic';
|
||||
export const revalidate = 0;
|
||||
|
||||
const MARZBAN_API = MARZBAN_PANEL_URL;
|
||||
const ADMIN_USERNAME = process.env.MARZBAN_ADMIN_USERNAME;
|
||||
const ADMIN_PASSWORD = process.env.MARZBAN_ADMIN_PASSWORD;
|
||||
|
||||
if (!ADMIN_USERNAME || !ADMIN_PASSWORD) {
|
||||
throw new Error('MARZBAN_ADMIN_USERNAME and MARZBAN_ADMIN_PASSWORD must be set in .env');
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const telegramId = searchParams.get('telegramId');
|
||||
const telegramUsername = searchParams.get('telegramUsername');
|
||||
|
||||
if (!telegramId && !telegramUsername) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'telegramId or telegramUsername required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 1. Получаем токен админа
|
||||
const tokenResponse = await fetch(`${MARZBAN_API}/api/admin/token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `grant_type=password&username=${ADMIN_USERNAME}&password=${ADMIN_PASSWORD}`,
|
||||
// Кешируем токен на 30 минут (стандартное время жизни JWT)
|
||||
cache: 'force-cache',
|
||||
next: { revalidate: 1800 }
|
||||
});
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
throw new Error('Failed to get admin token');
|
||||
}
|
||||
|
||||
const { access_token } = await tokenResponse.json();
|
||||
|
||||
// 2. Получаем ВСЕХ пользователей (Marzban API не поддерживает фильтр по note)
|
||||
const usersResponse = await fetch(`${MARZBAN_API}/api/users?offset=0&limit=1000`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${access_token}`,
|
||||
},
|
||||
// Данные пользователей НЕ кешируем - нужны в реальном времени
|
||||
cache: 'no-store',
|
||||
next: { revalidate: 0 }
|
||||
});
|
||||
|
||||
if (!usersResponse.ok) {
|
||||
throw new Error('Failed to get users');
|
||||
}
|
||||
|
||||
const usersData = await usersResponse.json();
|
||||
|
||||
logger.debug(`🔍 Searching for telegramId: ${telegramId}, username: ${telegramUsername}`);
|
||||
logger.debug(`📊 Total users in Marzban: ${usersData.users?.length || 0}`);
|
||||
|
||||
// 3. Ищем пользователя по Telegram ID в note или по username
|
||||
const user: MarzbanUser | undefined = usersData.users?.find((u: any) => {
|
||||
// Поиск по note: "TG: 1270320642"
|
||||
if (telegramId && u.note?.includes(`TG: ${telegramId}`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Поиск по username patterns
|
||||
if (telegramUsername) {
|
||||
const cleanUsername = telegramUsername.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
||||
if (u.username === cleanUsername) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Поиск по username с ID: "user_1270320642" или "имя_1270320642"
|
||||
if (telegramId && u.username?.includes(`_${telegramId}`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
logger.debug('❌ User not found');
|
||||
return NextResponse.json(
|
||||
{ success: false, hasSubscription: false },
|
||||
{ status: 200 }
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(`✅ Found user: ${user.username}`);
|
||||
|
||||
// 4. Получаем subscription token из subscription_url
|
||||
const subscriptionToken = user.subscription_url?.split('/sub/')[1]?.replace(/\/$/, '') || user.username;
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
hasSubscription: true,
|
||||
token: subscriptionToken,
|
||||
username: user.username,
|
||||
status: user.status,
|
||||
expire: user.expire,
|
||||
dataLimit: user.data_limit,
|
||||
dataLimitResetStrategy: user.data_limit_reset_strategy,
|
||||
usedTraffic: user.used_traffic,
|
||||
subscriptionUrl: getSubscriptionUrl(subscriptionToken),
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Get subscription error:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user