1181 lines
29 KiB
TypeScript
1181 lines
29 KiB
TypeScript
export const API_BASE_URL = 'https://minecraft.api.popa-popa.ru';
|
||
|
||
export interface Player {
|
||
uuid: string;
|
||
username: string;
|
||
skin_url: string;
|
||
cloak_url: string;
|
||
coins: number;
|
||
is_active: boolean;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface CoinsResponse {
|
||
username: string;
|
||
coins: number;
|
||
total_time_played: {
|
||
seconds: number;
|
||
formatted: string;
|
||
};
|
||
}
|
||
|
||
export interface Cape {
|
||
cape_id: string;
|
||
cape_name: string;
|
||
cape_description: string;
|
||
image_url: string;
|
||
purchased_at: string;
|
||
is_active: boolean;
|
||
}
|
||
|
||
export type CapesResponse = Cape[];
|
||
|
||
export interface StoreCape {
|
||
id: string;
|
||
name: string;
|
||
description: string;
|
||
price: number;
|
||
image_url: string;
|
||
}
|
||
|
||
export type StoreCapesResponse = StoreCape[];
|
||
|
||
export interface ApiError {
|
||
message: string;
|
||
details?: string;
|
||
}
|
||
|
||
export interface Server {
|
||
id: string;
|
||
name: string;
|
||
ip: string;
|
||
port: number;
|
||
description: string;
|
||
online_players: number;
|
||
max_players: number;
|
||
last_activity: string;
|
||
}
|
||
|
||
export interface ActiveServersResponse {
|
||
servers: Server[];
|
||
}
|
||
|
||
export interface OnlinePlayersResponse {
|
||
server: {
|
||
id: string;
|
||
name: string;
|
||
};
|
||
online_players: {
|
||
// Это массив объектов, а не один объект
|
||
username: string;
|
||
uuid: string;
|
||
online_since: string;
|
||
}[]; // Добавьте [] здесь чтобы указать, что это массив
|
||
count: number;
|
||
}
|
||
|
||
export interface MarketplaceResponse {
|
||
items: [
|
||
{
|
||
_id: string;
|
||
id: string;
|
||
material: string;
|
||
amount: number;
|
||
price: number;
|
||
seller_name: string;
|
||
server_ip: string;
|
||
display_name: string | null;
|
||
lore: string | null;
|
||
enchants: string | null;
|
||
item_data: {
|
||
slot: number;
|
||
material: string;
|
||
amount: number;
|
||
};
|
||
created_at: string;
|
||
},
|
||
];
|
||
total: number;
|
||
page: number;
|
||
pages: number;
|
||
}
|
||
|
||
export interface MarketplaceItemResponse {
|
||
_id: string;
|
||
id: string;
|
||
material: string;
|
||
amount: number;
|
||
price: number;
|
||
seller_name: string;
|
||
server_ip: string;
|
||
display_name: string | null;
|
||
lore: string | null;
|
||
enchants: string | null;
|
||
item_data: {
|
||
slot: number;
|
||
material: string;
|
||
amount: number;
|
||
};
|
||
created_at: string;
|
||
}
|
||
|
||
export interface SellItemResponse {
|
||
message: string;
|
||
}
|
||
|
||
export interface BuyItemResponse {
|
||
message: string;
|
||
}
|
||
|
||
export interface PlayerInventoryResponse {
|
||
status: string;
|
||
request_id: string;
|
||
}
|
||
|
||
export interface PlayerInventory {
|
||
status: string;
|
||
result: {
|
||
player_name: string;
|
||
server_ip: string;
|
||
inventory_data: PlayerInventoryItem[];
|
||
};
|
||
}
|
||
|
||
export interface PlayerInventoryItem {
|
||
slot: number;
|
||
material: string;
|
||
amount: number;
|
||
enchants: {
|
||
[key: string]: number;
|
||
};
|
||
}
|
||
|
||
export interface MarketplaceOperation {
|
||
id: string;
|
||
type: 'sell' | 'buy';
|
||
player_name: string;
|
||
slot_index?: number;
|
||
amount?: number;
|
||
price: number;
|
||
server_ip: string;
|
||
status: 'pending' | 'completed' | 'failed';
|
||
item_id?: string;
|
||
error?: string;
|
||
created_at: string;
|
||
item_data?: any;
|
||
}
|
||
|
||
export interface OperationsResponse {
|
||
operations: MarketplaceOperation[];
|
||
}
|
||
|
||
export interface RegisterUserResponse {
|
||
status: string;
|
||
uuid: string;
|
||
}
|
||
|
||
export interface GenerateVerificationCodeResponse {
|
||
status: string;
|
||
code: string;
|
||
}
|
||
|
||
export interface VerificationStatusResponse {
|
||
is_verified: boolean;
|
||
}
|
||
|
||
export interface NewsItem {
|
||
id: string;
|
||
title: string;
|
||
markdown: string;
|
||
preview?: string;
|
||
tags?: string[];
|
||
is_published?: boolean;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
export interface CreateNewsPayload {
|
||
title: string;
|
||
preview?: string;
|
||
markdown: string;
|
||
is_published: boolean;
|
||
}
|
||
|
||
export interface MeResponse {
|
||
username: string;
|
||
uuid: string;
|
||
is_admin: boolean;
|
||
}
|
||
|
||
// ===== БОНУСЫ / ПРОКАЧКА =====
|
||
|
||
export interface UserBonus {
|
||
id: string;
|
||
bonus_type_id: string;
|
||
name: string;
|
||
description: string;
|
||
effect_type: string;
|
||
effect_value: number;
|
||
level: number;
|
||
purchased_at: string;
|
||
can_upgrade: boolean;
|
||
upgrade_price: number;
|
||
is_active: boolean;
|
||
is_permanent: boolean;
|
||
expires_at?: string;
|
||
image_url?: string;
|
||
}
|
||
|
||
export type UserBonusesResponse = {
|
||
bonuses: UserBonus[];
|
||
};
|
||
|
||
export interface BonusType {
|
||
id: string;
|
||
name: string;
|
||
description: string;
|
||
effect_type: string;
|
||
base_effect_value: number;
|
||
effect_increment: number;
|
||
price: number;
|
||
upgrade_price: number;
|
||
duration: number;
|
||
max_level: number;
|
||
image_url?: string;
|
||
}
|
||
|
||
export type BonusTypesResponse = {
|
||
bonuses: BonusType[];
|
||
};
|
||
|
||
export interface DailyQuest {
|
||
key: string;
|
||
title: string;
|
||
event?: string;
|
||
target?: string;
|
||
required: number;
|
||
progress: number;
|
||
reward: number;
|
||
status: 'active' | 'completed' | 'claimed';
|
||
claimed_at?: string;
|
||
completed_at?: string;
|
||
}
|
||
|
||
export interface DailyQuestsStatusResponse {
|
||
ok: boolean;
|
||
day: string;
|
||
was_online_today: boolean;
|
||
seconds_to_next: number;
|
||
next_reset_at_utc: string;
|
||
next_reset_at_local: string;
|
||
quests: DailyQuest[];
|
||
}
|
||
|
||
export interface DailyQuestClaimResponse {
|
||
claimed: boolean;
|
||
coins_added?: number;
|
||
reason?: string;
|
||
message?: string;
|
||
}
|
||
|
||
export async function fetchBonusTypes(): Promise<BonusType[]> {
|
||
const response = await fetch(`${API_BASE_URL}/api/bonuses/types`);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить список прокачек');
|
||
}
|
||
|
||
const data: BonusTypesResponse = await response.json();
|
||
return data.bonuses || [];
|
||
}
|
||
|
||
export async function fetchUserBonuses(username: string): Promise<UserBonus[]> {
|
||
const response = await fetch(`${API_BASE_URL}/api/bonuses/user/${username}`);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить бонусы игрока');
|
||
}
|
||
|
||
const data: UserBonusesResponse = await response.json();
|
||
return data.bonuses || [];
|
||
}
|
||
|
||
export async function purchaseBonus(
|
||
username: string,
|
||
bonus_type_id: string,
|
||
): Promise<{
|
||
status: string;
|
||
message: string;
|
||
remaining_coins?: number;
|
||
}> {
|
||
const response = await fetch(`${API_BASE_URL}/api/bonuses/purchase`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
username,
|
||
bonus_type_id,
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось купить прокачку';
|
||
|
||
try {
|
||
const errorData = await response.json();
|
||
if (errorData.message) msg = errorData.message;
|
||
else if (Array.isArray(errorData.detail)) {
|
||
msg = errorData.detail.map((d: any) => d.msg).join(', ');
|
||
} else if (typeof errorData.detail === 'string') {
|
||
msg = errorData.detail;
|
||
}
|
||
} catch {
|
||
// оставляем дефолтное сообщение
|
||
}
|
||
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function upgradeBonus(
|
||
username: string,
|
||
bonus_id: string,
|
||
): Promise<{
|
||
status: string;
|
||
message: string;
|
||
bonus_id: string;
|
||
new_level?: number;
|
||
}> {
|
||
const response = await fetch(`${API_BASE_URL}/api/bonuses/upgrade`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
username,
|
||
bonus_id,
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось улучшить бонус';
|
||
|
||
try {
|
||
const errorData = await response.json();
|
||
if (errorData.message) msg = errorData.message;
|
||
else if (Array.isArray(errorData.detail)) {
|
||
msg = errorData.detail.map((d: any) => d.msg).join(', ');
|
||
} else if (typeof errorData.detail === 'string') {
|
||
msg = errorData.detail;
|
||
}
|
||
} catch {
|
||
// оставляем дефолтное сообщение
|
||
}
|
||
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function toggleBonusActivation(
|
||
username: string,
|
||
bonus_id: string,
|
||
): Promise<{
|
||
status: string;
|
||
message: string;
|
||
bonus_id: string;
|
||
is_active: boolean;
|
||
}> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/bonuses/toggle-activation`,
|
||
{
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
username,
|
||
bonus_id,
|
||
}),
|
||
},
|
||
);
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось переключить бонус';
|
||
|
||
try {
|
||
const errorData = await response.json();
|
||
if (errorData.message) msg = errorData.message;
|
||
else if (Array.isArray(errorData.detail)) {
|
||
msg = errorData.detail.map((d: any) => d.msg).join(', ');
|
||
} else if (typeof errorData.detail === 'string') {
|
||
msg = errorData.detail;
|
||
}
|
||
} catch {
|
||
// оставляем дефолтное сообщение
|
||
}
|
||
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
// ===== КЕЙСЫ =====
|
||
|
||
export interface CaseItemMeta {
|
||
display_name?: string | null;
|
||
lore?: string[] | null;
|
||
}
|
||
|
||
export interface CaseItem {
|
||
id: string;
|
||
name?: string;
|
||
material: string;
|
||
amount: number;
|
||
weight?: number;
|
||
meta?: CaseItemMeta;
|
||
}
|
||
|
||
export interface Case {
|
||
id: string;
|
||
name: string;
|
||
description?: string;
|
||
price: number;
|
||
image_url?: string;
|
||
server_ids?: string[];
|
||
items_count?: number;
|
||
items?: CaseItem[];
|
||
}
|
||
|
||
export interface OpenCaseResponse {
|
||
status: string;
|
||
message: string;
|
||
operation_id: string;
|
||
balance: number;
|
||
reward: CaseItem;
|
||
}
|
||
|
||
export async function fetchCases(): Promise<Case[]> {
|
||
const response = await fetch(`${API_BASE_URL}/cases`);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить список кейсов');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
// Если у тебя есть отдельный эндпоинт деталей кейса, можно использовать это:
|
||
export async function fetchCase(case_id: string): Promise<Case> {
|
||
const response = await fetch(`${API_BASE_URL}/cases/${case_id}`);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить информацию о кейсе');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function openCase(
|
||
case_id: string,
|
||
username: string,
|
||
server_id: string,
|
||
): Promise<OpenCaseResponse> {
|
||
// Формируем URL с query-параметрами, как любит текущий бэкенд
|
||
const url = new URL(`${API_BASE_URL}/cases/${case_id}/open`);
|
||
url.searchParams.append('username', username);
|
||
url.searchParams.append('server_id', server_id);
|
||
|
||
const response = await fetch(url.toString(), {
|
||
method: 'POST',
|
||
});
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось открыть кейс';
|
||
|
||
try {
|
||
const errorData = await response.json();
|
||
if (errorData.message) {
|
||
msg = errorData.message;
|
||
} else if (Array.isArray(errorData.detail)) {
|
||
msg = errorData.detail.map((d: any) => d.msg).join(', ');
|
||
} else if (typeof errorData.detail === 'string') {
|
||
msg = errorData.detail;
|
||
}
|
||
} catch {
|
||
// если бэкенд вернул не-JSON, оставляем дефолтное сообщение
|
||
}
|
||
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
// ===== КЕЙСЫ ===== \\
|
||
|
||
// ===== Ежедневная награда =====
|
||
export interface DailyStatusResponse {
|
||
ok: boolean;
|
||
can_claim: boolean;
|
||
seconds_to_next: number;
|
||
was_online_today: boolean;
|
||
next_claim_at_utc: string;
|
||
next_claim_at_local: string;
|
||
streak: number;
|
||
}
|
||
|
||
export interface DailyClaimResponse {
|
||
claimed: boolean;
|
||
coins_added?: number;
|
||
streak?: number;
|
||
reason?: string;
|
||
}
|
||
|
||
export interface DailyDaysResponse {
|
||
ok: boolean;
|
||
days: string[]; // YYYY-MM-DD (по ЕКБ)
|
||
count: number;
|
||
}
|
||
|
||
export async function fetchDailyClaimDays(limit = 120): Promise<DailyDaysResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({
|
||
accessToken,
|
||
clientToken,
|
||
limit: String(limit),
|
||
});
|
||
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/users/daily/days?${params.toString()}`,
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить дни ежедневных наград');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function fetchDailyStatus(): Promise<DailyStatusResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({ accessToken, clientToken });
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/users/daily/status?${params.toString()}`,
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить статус ежедневной награды');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function claimDaily(): Promise<DailyClaimResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({ accessToken, clientToken });
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/users/daily/claim?${params.toString()}`,
|
||
{ method: 'POST' },
|
||
);
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось забрать ежедневную награду';
|
||
try {
|
||
const err = await response.json();
|
||
msg = err.message || err.detail || msg;
|
||
} catch {}
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
// ===== Ежедневная награда ===== \\
|
||
|
||
// ===== Ежедневные квесты =====
|
||
|
||
export async function fetchDailyQuestsStatus(): Promise<DailyQuestsStatusResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({ accessToken, clientToken });
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/users/daily-quests/status?${params.toString()}`,
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить статус ежедневных квестов');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function claimDailyQuest(questKey: string): Promise<DailyQuestClaimResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({ accessToken, clientToken, quest_key: questKey });
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/users/daily-quests/claim?${params.toString()}`,
|
||
{ method: 'POST' },
|
||
);
|
||
|
||
if (!response.ok) {
|
||
let msg = 'Не удалось забрать награду за квест';
|
||
try {
|
||
const err = await response.json();
|
||
msg = err.message || err.detail || msg;
|
||
} catch {}
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
// ===== Ежедневные квесты ===== \\
|
||
|
||
export async function fetchMe(): Promise<MeResponse> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Нет токенов авторизации');
|
||
}
|
||
|
||
const params = new URLSearchParams({
|
||
accessToken,
|
||
clientToken,
|
||
});
|
||
|
||
const response = await fetch(`${API_BASE_URL}/auth/me?${params.toString()}`);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить данные пользователя');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function createNews(payload: CreateNewsPayload) {
|
||
const { accessToken, clientToken } = getAuthTokens(); // ← используем launcher_config
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Необходимо войти в лаунчер, чтобы публиковать новости');
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('accessToken', accessToken);
|
||
formData.append('clientToken', clientToken);
|
||
formData.append('title', payload.title);
|
||
formData.append('markdown', payload.markdown);
|
||
formData.append('preview', payload.preview || '');
|
||
formData.append('is_published', String(payload.is_published));
|
||
|
||
const response = await fetch(`${API_BASE_URL}/news`, {
|
||
method: 'POST',
|
||
body: formData,
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const text = await response.text();
|
||
throw new Error(text || 'Ошибка при создании новости');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function deleteNews(id: string): Promise<void> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
if (!accessToken || !clientToken) {
|
||
throw new Error('Необходимо войти в лаунчер');
|
||
}
|
||
|
||
const params = new URLSearchParams({
|
||
accessToken,
|
||
clientToken,
|
||
});
|
||
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/news/${id}?${params.toString()}`,
|
||
{
|
||
method: 'DELETE',
|
||
},
|
||
);
|
||
|
||
if (!response.ok) {
|
||
const text = await response.text();
|
||
throw new Error(text || 'Не удалось удалить новость');
|
||
}
|
||
}
|
||
|
||
export async function fetchNews(): Promise<NewsItem[]> {
|
||
const response = await fetch(`${API_BASE_URL}/news`);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить новости');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function getVerificationStatus(
|
||
username: string,
|
||
): Promise<VerificationStatusResponse> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/auth/verification_status/${username}`,
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить статус верификации');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function generateVerificationCode(
|
||
username: string,
|
||
): Promise<GenerateVerificationCodeResponse> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/auth/generate_code?username=${username}`,
|
||
{
|
||
method: 'POST',
|
||
},
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось сгенерировать код верификации');
|
||
}
|
||
return await response.json();
|
||
}
|
||
export async function registerUser(
|
||
username: string,
|
||
password: string,
|
||
): Promise<RegisterUserResponse> {
|
||
const response = await fetch(`${API_BASE_URL}/auth/register`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
username,
|
||
password,
|
||
}),
|
||
});
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось зарегистрировать пользователя');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function getPlayerInventory(
|
||
request_id: string,
|
||
): Promise<PlayerInventory> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/server/inventory/${request_id}`,
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить инвентарь игрока');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function RequestPlayerInventory(
|
||
server_ip: string,
|
||
player_name: string,
|
||
): Promise<PlayerInventoryResponse> {
|
||
const response = await fetch(`${API_BASE_URL}/api/server/inventory`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
server_ip: server_ip,
|
||
player_name: player_name,
|
||
}),
|
||
});
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить инвентарь игрока');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function buyItem(
|
||
buyer_username: string,
|
||
item_id: string,
|
||
): Promise<{ status: string; operation_id: string; message: string }> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/marketplace/items/buy/${item_id}`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
username: buyer_username,
|
||
}),
|
||
},
|
||
);
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(
|
||
errorData.message || errorData.detail || 'Не удалось купить предмет',
|
||
);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function confirmMarketplaceOperation(
|
||
operation_id: string,
|
||
status: string = 'success',
|
||
error?: string,
|
||
): Promise<{ status: string }> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/marketplace/operations/confirm`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
operation_id,
|
||
status,
|
||
error,
|
||
}),
|
||
},
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось подтвердить операцию');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function submitItemDetails(
|
||
operation_id: string,
|
||
item_data: any,
|
||
): Promise<{ status: string }> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/marketplace/items/details`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
operation_id,
|
||
item_data,
|
||
}),
|
||
},
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось отправить данные предмета');
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function sellItem(
|
||
username: string,
|
||
slot_index: number,
|
||
amount: number,
|
||
price: number,
|
||
server_ip: string,
|
||
): Promise<{ status: string; operation_id: string }> {
|
||
const response = await fetch(`${API_BASE_URL}/api/marketplace/items/sell`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
username,
|
||
slot_index,
|
||
amount,
|
||
price,
|
||
server_ip,
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(
|
||
errorData.message ||
|
||
errorData.detail ||
|
||
'Не удалось выставить предмет на продажу',
|
||
);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
export async function fetchMarketplaceItem(
|
||
item_id: string,
|
||
): Promise<MarketplaceItemResponse> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/marketplace/items/${item_id}`,
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить рынок');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function fetchMarketplace(
|
||
server_ip: string,
|
||
page: number,
|
||
limit: number,
|
||
): Promise<MarketplaceResponse> {
|
||
// Создаем URL с параметрами запроса
|
||
const url = new URL(`${API_BASE_URL}/api/marketplace/items`);
|
||
url.searchParams.append('server_ip', server_ip);
|
||
url.searchParams.append('page', page.toString());
|
||
url.searchParams.append('limit', limit.toString());
|
||
|
||
const response = await fetch(url.toString());
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить предметы рынка');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
// Исправьте тип возвращаемого значения
|
||
export async function fetchActiveServers(): Promise<Server[]> {
|
||
const response = await fetch(`${API_BASE_URL}/api/pranks/servers`);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить активные сервера');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
export async function fetchOnlinePlayers(
|
||
server_id: string,
|
||
): Promise<OnlinePlayersResponse> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/api/pranks/servers/${server_id}/players`,
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось получить онлайн игроков');
|
||
}
|
||
return await response.json();
|
||
}
|
||
|
||
// Получение информации о игроке
|
||
export async function fetchPlayer(uuid: string): Promise<Player> {
|
||
try {
|
||
const response = await fetch(`${API_BASE_URL}/users/${uuid}`);
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.message || 'Ошибка получения данных игрока');
|
||
}
|
||
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API ошибка:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
export async function fetchCoins(username: string): Promise<CoinsResponse> {
|
||
try {
|
||
const response = await fetch(`${API_BASE_URL}/users/${username}/coins`);
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.message || 'Ошибка получения данных игрока');
|
||
}
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API ошибка:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
export async function fetchCapes(username: string): Promise<CapesResponse> {
|
||
try {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/store/user/${username}/capes`,
|
||
);
|
||
if (!response.ok) {
|
||
return []; // Если плащи не найдены, возвращаем пустой массив
|
||
}
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API ошибка:', error);
|
||
return []; // В случае ошибки возвращаем пустой массив
|
||
}
|
||
}
|
||
|
||
export async function purchaseCape(
|
||
username: string,
|
||
cape_id: string,
|
||
): Promise<void> {
|
||
// Создаем URL с query-параметрами
|
||
const url = new URL(`${API_BASE_URL}/store/purchase/cape`);
|
||
url.searchParams.append('username', username);
|
||
url.searchParams.append('cape_id', cape_id);
|
||
|
||
const response = await fetch(url.toString(), {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
// Не нужно отправлять тело запроса
|
||
// body: JSON.stringify({
|
||
// username: username,
|
||
// cape_id: cape_id,
|
||
// }),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(
|
||
errorData.message ||
|
||
errorData.detail?.toString() ||
|
||
'Не удалось купить плащ',
|
||
);
|
||
}
|
||
}
|
||
|
||
export async function fetchCapesStore(): Promise<StoreCape[]> {
|
||
try {
|
||
const response = await fetch(`${API_BASE_URL}/store/capes`);
|
||
if (!response.ok) {
|
||
return [];
|
||
}
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API ошибка:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
export async function activateCape(
|
||
username: string,
|
||
cape_id: string,
|
||
): Promise<void> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/store/user/${username}/capes/activate/${cape_id}`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
cape_id: cape_id,
|
||
}),
|
||
},
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось активировать плащ');
|
||
}
|
||
}
|
||
|
||
export async function deactivateCape(
|
||
username: string,
|
||
cape_id: string,
|
||
): Promise<void> {
|
||
const response = await fetch(
|
||
`${API_BASE_URL}/store/user/${username}/capes/deactivate/${cape_id}`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
cape_id: cape_id,
|
||
}),
|
||
},
|
||
);
|
||
if (!response.ok) {
|
||
throw new Error('Не удалось деактивировать плащ');
|
||
}
|
||
}
|
||
|
||
// Загрузка скина
|
||
export async function uploadSkin(
|
||
username: string,
|
||
skinFile: File,
|
||
skinModel: string,
|
||
): Promise<void> {
|
||
const savedConfig = localStorage.getItem('launcher_config');
|
||
let accessToken = '';
|
||
let clientToken = '';
|
||
|
||
if (savedConfig) {
|
||
const config = JSON.parse(savedConfig);
|
||
accessToken = config.accessToken || '';
|
||
clientToken = config.clientToken || '';
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('skin_file', skinFile);
|
||
formData.append('skin_model', skinModel);
|
||
formData.append('accessToken', accessToken);
|
||
formData.append('clientToken', clientToken);
|
||
|
||
const response = await fetch(`${API_BASE_URL}/user/${username}/skin`, {
|
||
method: 'POST',
|
||
body: formData,
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.message || 'Не удалось загрузить скин');
|
||
}
|
||
}
|
||
|
||
// Получение токенов из локального хранилища
|
||
export function getAuthTokens(): { accessToken: string; clientToken: string } {
|
||
const savedConfig = localStorage.getItem('launcher_config');
|
||
let accessToken = '';
|
||
let clientToken = '';
|
||
|
||
if (savedConfig) {
|
||
const config = JSON.parse(savedConfig);
|
||
accessToken = config.accessToken || '';
|
||
clientToken = config.clientToken || '';
|
||
}
|
||
|
||
return { accessToken, clientToken };
|
||
}
|
||
|
||
// Загрузка плаща
|
||
export async function uploadCape(
|
||
username: string,
|
||
capeFile: File,
|
||
): Promise<void> {
|
||
const { accessToken, clientToken } = getAuthTokens();
|
||
|
||
const formData = new FormData();
|
||
formData.append('cape_file', capeFile);
|
||
formData.append('accessToken', accessToken);
|
||
formData.append('clientToken', clientToken);
|
||
|
||
const response = await fetch(`${API_BASE_URL}/user/${username}/cape`, {
|
||
method: 'POST',
|
||
body: formData,
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.message || 'Не удалось загрузить плащ');
|
||
}
|
||
}
|