'Configuration not found']); exit; } $config = require $configFile; // Autoload classes spl_autoload_register(function ($class) { $file = __DIR__ . '/classes/' . $class . '.php'; if (file_exists($file)) { require_once $file; } }); /** * Create FlickrAPI instance with OAuth if available */ function createFlickrAPI($config) { $flickr = new FlickrAPI( $config['flickr']['api_key'], $config['flickr']['api_secret'] ?? '', $config['flickr_user_id'] ?? '' ); // Add OAuth if tokens exist if (!empty($config['flickr']['api_secret'])) { $oauth = new FlickrOAuth( $config['flickr']['api_key'], $config['flickr']['api_secret'] ); if ($oauth->isAuthorized()) { $flickr->setOAuth($oauth); } } return $flickr; } // Check authentication $auth = new Auth(); if (!$auth->isAuthenticated()) { http_response_code(401); echo json_encode(['error' => 'Not authenticated']); exit; } // Get action $action = $_GET['action'] ?? $_POST['action'] ?? ''; try { switch ($action) { // ============ CONVERTER ============ case 'convert': $urls = $_POST['urls'] ?? ''; $size = $_POST['size'] ?? 'Large'; $format = $_POST['format'] ?? 'bbcode'; $parser = new FlickrParser(); $generator = new FormatGenerator(); $parsed = $parser->parseMultiple($urls); if (empty($parsed)) { echo json_encode(['error' => 'No valid Flickr URLs found']); exit; } $images = []; $flickr = null; // Check if we need API to get full URLs $needApi = false; foreach ($parsed as $item) { if ($item['type'] === 'page' || $item['type'] === 'short') { $needApi = true; break; } } if ($needApi && !empty($config['flickr']['api_key'])) { $flickr = createFlickrAPI($config); } foreach ($parsed as $item) { $imageData = [ 'photo_id' => $item['photo_id'], 'title' => 'Image', ]; if ($item['type'] === 'direct') { // Direct URL - modify size suffix $imageData['url'] = $parser->buildImageUrl($item, $size); $imageData['original'] = $parser->buildImageUrl($item, 'Original'); } elseif ($flickr) { // Need to fetch from API try { $info = $flickr->getPhotoInfo($item['photo_id']); $sizes = $flickr->getPhotoSizes($item['photo_id']); $imageData['title'] = $info['title']['_content'] ?? 'Image'; // Get requested size $sizeMap = [ 'Large' => 'Large', 'Large1600' => 'Large 1600', 'Large2048' => 'Large 2048', 'Original' => 'Original', 'Medium640' => 'Medium 640', 'Medium' => 'Medium', ]; $sizeName = $sizeMap[$size] ?? 'Large'; $imageData['url'] = $sizes[$sizeName]['url'] ?? $sizes['Large']['url'] ?? ''; $imageData['original'] = $sizes['Original']['url'] ?? $sizes['Large']['url'] ?? ''; } catch (Exception $e) { $imageData['url'] = $item['original_url']; $imageData['original'] = $item['original_url']; } } else { // No API - use original URL $imageData['url'] = $item['original_url']; $imageData['original'] = $item['original_url']; } $images[] = $imageData; } $output = $generator->generateMultiple($format, $images); echo json_encode([ 'success' => true, 'output' => $output, 'count' => count($images), ]); break; // ============ FLICKR GALLERY ============ case 'flickr_albums': if (empty($config['flickr']['api_key'])) { echo json_encode(['error' => 'Flickr API not configured']); exit; } $flickr = createFlickrAPI($config); $page = (int)($_GET['page'] ?? 1); $perPage = (int)($_GET['per_page'] ?? 50); $result = $flickr->getPhotosets($page, $perPage); echo json_encode([ 'success' => true, 'albums' => $result['albums'], 'page' => $result['page'], 'pages' => $result['pages'], 'total' => $result['total'], ]); break; case 'flickr_photos': if (empty($config['flickr']['api_key'])) { echo json_encode(['error' => 'Flickr API not configured']); exit; } $flickr = createFlickrAPI($config); $page = (int)($_GET['page'] ?? 1); $perPage = (int)($_GET['per_page'] ?? 50); $albumId = $_GET['album_id'] ?? ''; $search = $_GET['search'] ?? ''; if ($search) { $result = $flickr->searchPhotos($search, $page, $perPage); } elseif ($albumId) { $result = $flickr->getPhotosetPhotos($albumId, $page, $perPage); } else { $result = $flickr->getPhotos($page, $perPage); } echo json_encode([ 'success' => true, 'photos' => $result['photos'], 'pagination' => [ 'page' => $result['page'], 'pages' => $result['pages'], 'total' => $result['total'], ], ]); break; case 'flickr_photo_sizes': if (empty($config['flickr']['api_key'])) { echo json_encode(['error' => 'Flickr API not configured']); exit; } $photoId = $_GET['photo_id'] ?? ''; if (!$photoId) { echo json_encode(['error' => 'Photo ID required']); exit; } $flickr = createFlickrAPI($config); $sizes = $flickr->getPhotoSizes($photoId); echo json_encode([ 'success' => true, 'sizes' => $sizes, ]); break; case 'flickr_oauth_status': if (empty($config['flickr']['api_secret'])) { echo json_encode([ 'success' => true, 'authorized' => false, 'message' => 'API secret not configured', ]); exit; } $oauth = new FlickrOAuth( $config['flickr']['api_key'], $config['flickr']['api_secret'] ); echo json_encode([ 'success' => true, 'authorized' => $oauth->isAuthorized(), 'auth_url' => 'flickr_auth.php', ]); break; case 'flickr_original_url': if (empty($config['flickr']['api_key'])) { echo json_encode(['error' => 'Flickr API not configured']); exit; } $photoId = $_GET['photo_id'] ?? ''; if (!$photoId) { echo json_encode(['error' => 'Photo ID required']); exit; } $flickr = createFlickrAPI($config); $originalUrl = $flickr->getOriginalUrl($photoId); echo json_encode([ 'success' => true, 'original_url' => $originalUrl, 'has_oauth' => $flickr->hasOAuth(), ]); break; // ============ TELEGRAM ============ case 'telegram_status': if (empty($config['telegram']['bot_token'])) { echo json_encode([ 'success' => true, 'connected' => false, 'message' => 'Bot token not configured', ]); exit; } $telegram = new TelegramBot($config['telegram']['bot_token']); try { $me = $telegram->getMe(); echo json_encode([ 'success' => true, 'connected' => true, 'bot_name' => $me['first_name'] ?? '', 'bot_username' => $me['username'] ?? '', ]); } catch (Exception $e) { echo json_encode([ 'success' => true, 'connected' => false, 'message' => $e->getMessage(), ]); } break; case 'telegram_channels': echo json_encode([ 'success' => true, 'channels' => $config['telegram']['channels'] ?? [], ]); break; case 'telegram_post': if (empty($config['telegram']['bot_token'])) { echo json_encode(['error' => 'Telegram bot not configured']); exit; } $channelId = $_POST['channel_id'] ?? ''; $text = $_POST['text'] ?? ''; $photos = json_decode($_POST['photos'] ?? '[]', true); $parseMode = $_POST['parse_mode'] ?? 'HTML'; if (!$channelId) { echo json_encode(['error' => 'Channel ID required']); exit; } $telegram = new TelegramBot($config['telegram']['bot_token']); $result = $telegram->post($channelId, $photos, $text, $parseMode); echo json_encode([ 'success' => true, 'result' => $result, ]); break; // ============ VK ============ case 'vk_status': if (empty($config['vk']['access_token'])) { echo json_encode([ 'success' => true, 'connected' => false, 'message' => 'VK access token not configured', ]); exit; } $vk = new VKAPI($config['vk']['access_token']); try { $validation = $vk->validateToken(); if ($validation['valid']) { echo json_encode([ 'success' => true, 'connected' => true, 'user_name' => $validation['user_name'] ?? '', 'user_id' => $validation['user_id'] ?? '', 'type' => $validation['type'] ?? 'user', 'screen_name' => $validation['screen_name'] ?? '', ]); } else { echo json_encode([ 'success' => true, 'connected' => false, 'message' => $validation['error'] ?? 'Invalid token', ]); } } catch (Exception $e) { echo json_encode([ 'success' => true, 'connected' => false, 'message' => $e->getMessage(), ]); } break; case 'vk_groups': if (empty($config['vk']['access_token'])) { echo json_encode([ 'success' => true, 'groups' => [], ]); exit; } $vk = new VKAPI($config['vk']['access_token']); // First check if this is a community token try { $validation = $vk->validateToken(); if ($validation['valid'] && ($validation['type'] ?? '') === 'community') { // Community token - return the community itself echo json_encode([ 'success' => true, 'groups' => [[ 'id' => $validation['user_id'], 'name' => $validation['user_name'] ?? 'Сообщество', 'screen_name' => $validation['screen_name'] ?? '', ]], 'type' => 'community', ]); exit; } } catch (Exception $e) { // Continue to try groups.get } try { $groups = $vk->getGroups(); echo json_encode([ 'success' => true, 'groups' => $groups, ]); } catch (Exception $e) { echo json_encode([ 'success' => true, 'groups' => [], 'error' => $e->getMessage(), ]); } break; case 'vk_post': if (empty($config['vk']['access_token'])) { echo json_encode(['error' => 'VK not configured']); exit; } $groupId = $_POST['group_id'] ?? ''; $text = $_POST['text'] ?? ''; $photos = json_decode($_POST['photos'] ?? '[]', true); if (!$groupId) { echo json_encode(['error' => 'Group ID required']); exit; } $vk = new VKAPI($config['vk']['access_token']); $result = $vk->post($groupId, $photos, $text); echo json_encode([ 'success' => true, 'result' => $result, ]); break; // ============ FILE UPLOAD ============ case 'upload_file': if (empty($_FILES['file'])) { echo json_encode(['error' => 'No file uploaded']); exit; } $file = $_FILES['file']; $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4', 'video/quicktime', 'video/webm']; if (!in_array($file['type'], $allowedTypes)) { echo json_encode(['error' => 'Неподдерживаемый тип файла: ' . $file['type']]); exit; } if ($file['size'] > 50 * 1024 * 1024) { echo json_encode(['error' => 'Файл слишком большой (макс 50MB)']); exit; } // Create uploads directory $uploadsDir = __DIR__ . '/uploads'; if (!is_dir($uploadsDir)) { mkdir($uploadsDir, 0755, true); } // Generate unique filename $ext = pathinfo($file['name'], PATHINFO_EXTENSION); $filename = uniqid('upload_') . '_' . time() . '.' . $ext; $filepath = $uploadsDir . '/' . $filename; if (move_uploaded_file($file['tmp_name'], $filepath)) { // Get the URL $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $path = dirname($_SERVER['REQUEST_URI']); $url = $protocol . '://' . $host . $path . '/uploads/' . $filename; echo json_encode([ 'success' => true, 'url' => $url, 'filename' => $filename, 'type' => $file['type'], 'size' => $file['size'] ]); } else { echo json_encode(['error' => 'Не удалось сохранить файл']); } break; // ============ MULTI-PLATFORM POSTING ============ case 'multi_post': $platforms = json_decode($_POST['platforms'] ?? '[]', true); $text = $_POST['text'] ?? ''; $photos = json_decode($_POST['photos'] ?? '[]', true); $uploadedFiles = json_decode($_POST['uploaded_files'] ?? '[]', true); $parseMode = $_POST['parse_mode'] ?? 'HTML'; // Merge Flickr photos and uploaded files if (!empty($uploadedFiles)) { foreach ($uploadedFiles as $uploadedFile) { if (!empty($uploadedFile['url'])) { $photos[] = $uploadedFile['url']; } } } if (empty($platforms)) { echo json_encode(['error' => 'No platforms selected']); exit; } $results = []; foreach ($platforms as $platform) { $type = $platform['type'] ?? ''; $target = $platform['target'] ?? ''; switch ($type) { case 'telegram': if (empty($config['telegram']['bot_token'])) { $results['telegram'] = [ 'success' => false, 'error' => 'Telegram not configured', ]; break; } try { $telegram = new TelegramBot($config['telegram']['bot_token']); $result = $telegram->post($target, $photos, $text, $parseMode); $results['telegram'] = [ 'success' => true, 'result' => $result, ]; } catch (Exception $e) { $results['telegram'] = [ 'success' => false, 'error' => $e->getMessage(), ]; } break; case 'vk': if (empty($config['vk']['access_token'])) { $results['vk'] = [ 'success' => false, 'error' => 'VK not configured', ]; break; } try { $vk = new VKAPI($config['vk']['access_token']); // VK uses plain text, remove HTML/Markdown formatting $vkText = strip_tags($text); $result = $vk->post($target, $photos, $vkText); $vkResult = [ 'success' => true, 'result' => $result, ]; // Include warning if photos were posted as links if (isset($result['warning'])) { $vkResult['warning'] = $result['warning']; } $results['vk'] = $vkResult; } catch (Exception $e) { $results['vk'] = [ 'success' => false, 'error' => $e->getMessage(), ]; } break; case 'instagram': // Instagram requires Facebook Business API $results['instagram'] = [ 'success' => false, 'error' => 'Instagram posting requires Facebook Business API setup', ]; break; default: $results[$type] = [ 'success' => false, 'error' => 'Unknown platform', ]; } } echo json_encode([ 'success' => true, 'results' => $results, ]); break; // ============ TAG PRESETS ============ case 'get_presets': $presetsFile = __DIR__ . '/data/tag_presets.json'; if (file_exists($presetsFile)) { $presets = json_decode(file_get_contents($presetsFile), true); echo json_encode(['success' => true, 'presets' => $presets ?: []]); } else { // Return default presets $defaultPresets = [ ['id' => 1, 'name' => 'BJD', 'tags' => ['bjd', 'doll', 'куклы']], ['id' => 2, 'name' => 'Фото', 'tags' => ['фото', 'photo', 'photography']], ['id' => 3, 'name' => 'Арт', 'tags' => ['art', 'artwork', 'творчество']], ['id' => 4, 'name' => 'Handmade', 'tags' => ['handmade', 'ручнаяработа']], ['id' => 5, 'name' => 'Faceup', 'tags' => ['faceup', 'мейк']], ['id' => 6, 'name' => 'Outfit', 'tags' => ['outfit', 'одежда']], ]; echo json_encode(['success' => true, 'presets' => $defaultPresets]); } break; case 'save_presets': $presetsFile = __DIR__ . '/data/tag_presets.json'; $input = json_decode(file_get_contents('php://input'), true); $presets = $input['presets'] ?? []; // Validate presets structure if (!is_array($presets)) { echo json_encode(['error' => 'Invalid presets format']); exit; } // Ensure data directory exists $dataDir = __DIR__ . '/data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } // Save presets if (file_put_contents($presetsFile, json_encode($presets, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true, 'message' => 'Presets saved']); } else { echo json_encode(['error' => 'Failed to save presets']); } break; // ============ SCHEDULED POSTS ============ case 'get_scheduled_posts': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; if (file_exists($scheduledFile)) { $posts = json_decode(file_get_contents($scheduledFile), true) ?: []; // Sort by scheduled time usort($posts, function($a, $b) { return strtotime($a['scheduled_time']) - strtotime($b['scheduled_time']); }); echo json_encode(['success' => true, 'posts' => $posts]); } else { echo json_encode(['success' => true, 'posts' => []]); } break; case 'create_scheduled_post': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; $dataDir = __DIR__ . '/data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } $posts = file_exists($scheduledFile) ? json_decode(file_get_contents($scheduledFile), true) ?: [] : []; $newPost = [ 'id' => uniqid('sched_'), 'text' => $_POST['text'] ?? '', 'tags' => json_decode($_POST['tags'] ?? '[]', true), 'photos' => json_decode($_POST['photos'] ?? '[]', true), 'uploaded_files' => json_decode($_POST['uploaded_files'] ?? '[]', true), 'platforms' => json_decode($_POST['platforms'] ?? '[]', true), 'scheduled_time' => $_POST['scheduled_time'] ?? '', 'cross_promo' => ($_POST['cross_promo'] ?? '0') === '1', 'created_at' => date('Y-m-d H:i:s'), 'status' => 'pending' ]; if (empty($newPost['scheduled_time'])) { echo json_encode(['error' => 'Укажите дату и время публикации']); exit; } if (strtotime($newPost['scheduled_time']) < time()) { echo json_encode(['error' => 'Нельзя запланировать на прошедшее время']); exit; } $posts[] = $newPost; if (file_put_contents($scheduledFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true, 'post' => $newPost]); } else { echo json_encode(['error' => 'Не удалось сохранить']); } break; case 'update_scheduled_post': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; $postId = $_POST['id'] ?? ''; if (!$postId) { echo json_encode(['error' => 'ID поста не указан']); exit; } $posts = file_exists($scheduledFile) ? json_decode(file_get_contents($scheduledFile), true) ?: [] : []; $found = false; foreach ($posts as &$post) { if ($post['id'] === $postId && $post['status'] === 'pending') { $post['text'] = $_POST['text'] ?? $post['text']; $post['tags'] = isset($_POST['tags']) ? json_decode($_POST['tags'], true) : $post['tags']; $post['photos'] = isset($_POST['photos']) ? json_decode($_POST['photos'], true) : $post['photos']; $post['uploaded_files'] = isset($_POST['uploaded_files']) ? json_decode($_POST['uploaded_files'], true) : $post['uploaded_files']; $post['platforms'] = isset($_POST['platforms']) ? json_decode($_POST['platforms'], true) : $post['platforms']; $post['scheduled_time'] = $_POST['scheduled_time'] ?? $post['scheduled_time']; $post['cross_promo'] = isset($_POST['cross_promo']) ? ($_POST['cross_promo'] === '1') : ($post['cross_promo'] ?? false); $post['updated_at'] = date('Y-m-d H:i:s'); $found = true; break; } } if (!$found) { echo json_encode(['error' => 'Пост не найден или уже опубликован']); exit; } if (file_put_contents($scheduledFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true, 'message' => 'Пост обновлён']); } else { echo json_encode(['error' => 'Не удалось сохранить']); } break; case 'delete_scheduled_post': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; $postId = $_POST['id'] ?? ''; if (!$postId) { echo json_encode(['error' => 'ID поста не указан']); exit; } $posts = file_exists($scheduledFile) ? json_decode(file_get_contents($scheduledFile), true) ?: [] : []; $initialCount = count($posts); $posts = array_filter($posts, function($post) use ($postId) { return $post['id'] !== $postId || $post['status'] !== 'pending'; }); if (count($posts) === $initialCount) { echo json_encode(['error' => 'Пост не найден или уже опубликован']); exit; } $posts = array_values($posts); // Re-index if (file_put_contents($scheduledFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true, 'message' => 'Пост удалён']); } else { echo json_encode(['error' => 'Не удалось сохранить']); } break; case 'mark_post_published': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; $postId = $_POST['id'] ?? ''; $resultsJson = $_POST['results'] ?? '{}'; $results = json_decode($resultsJson, true) ?: []; if (!$postId) { echo json_encode(['error' => 'ID поста не указан']); exit; } $posts = file_exists($scheduledFile) ? json_decode(file_get_contents($scheduledFile), true) ?: [] : []; $found = false; foreach ($posts as &$post) { if (($post['id'] ?? null) === $postId && ($post['status'] ?? '') === 'pending') { $post['status'] = 'published'; $post['published_at'] = date('Y-m-d H:i:s'); $post['results'] = $results; $found = true; break; } } unset($post); if (!$found) { echo json_encode(['error' => 'Пост не найден или уже опубликован']); exit; } if (file_put_contents($scheduledFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true]); } else { echo json_encode(['error' => 'Не удалось сохранить']); } break; case 'get_published_posts': $scheduledFile = __DIR__ . '/data/scheduled_posts.json'; if (file_exists($scheduledFile)) { $posts = json_decode(file_get_contents($scheduledFile), true) ?: []; // Filter only published posts $published = array_filter($posts, function($p) { return $p['status'] === 'published'; }); // Sort by published_at descending (newest first) usort($published, function($a, $b) { return strtotime($b['published_at'] ?? $b['scheduled_time']) - strtotime($a['published_at'] ?? $a['scheduled_time']); }); // Return last 10 $published = array_slice($published, 0, 10); echo json_encode(['success' => true, 'posts' => array_values($published)]); } else { echo json_encode(['success' => true, 'posts' => []]); } break; // ============ CROSS-PROMO SETTINGS ============ case 'save_cross_promo': $settingsFile = __DIR__ . '/data/cross_promo.json'; $dataDir = __DIR__ . '/data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } $settings = [ 'telegramLink' => trim($_POST['telegramLink'] ?? ''), 'vkLink' => trim($_POST['vkLink'] ?? ''), 'textForTg' => trim($_POST['textForTg'] ?? 'Мой канал ВКонтакте'), 'textForVk' => trim($_POST['textForVk'] ?? 'Мой канал в Telegram') ]; if (file_put_contents($settingsFile, json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true]); } else { echo json_encode(['error' => 'Не удалось сохранить']); } break; case 'get_cross_promo': $settingsFile = __DIR__ . '/data/cross_promo.json'; if (file_exists($settingsFile)) { $settings = json_decode(file_get_contents($settingsFile), true) ?: []; echo json_encode(['success' => true, 'settings' => $settings]); } else { echo json_encode(['success' => true, 'settings' => [ 'telegramLink' => '', 'vkLink' => '', 'textForTg' => 'Мой канал ВКонтакте', 'textForVk' => 'Мой канал в Telegram' ]]); } break; // ============ DRAFTS ============ case 'save_draft': $draftFile = __DIR__ . '/data/draft.json'; $dataDir = __DIR__ . '/data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } $draft = [ 'text' => $_POST['text'] ?? '', 'tags' => json_decode($_POST['tags'] ?? '[]', true), 'photos' => json_decode($_POST['photos'] ?? '[]', true), 'uploaded_files' => json_decode($_POST['uploaded_files'] ?? '[]', true), 'updated_at' => date('Y-m-d H:i:s') ]; if (file_put_contents($draftFile, json_encode($draft, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true]); } else { echo json_encode(['error' => 'Не удалось сохранить черновик']); } break; case 'get_draft': $draftFile = __DIR__ . '/data/draft.json'; if (file_exists($draftFile)) { $draft = json_decode(file_get_contents($draftFile), true) ?: []; echo json_encode(['success' => true, 'draft' => $draft]); } else { echo json_encode(['success' => true, 'draft' => null]); } break; case 'clear_draft': $draftFile = __DIR__ . '/data/draft.json'; if (file_exists($draftFile)) { unlink($draftFile); } echo json_encode(['success' => true]); break; // ============ SETTINGS ============ case 'save_vk_token': $token = trim($_POST['token'] ?? ''); if (empty($token)) { echo json_encode(['error' => 'Токен не может быть пустым']); exit; } // Update config.php with new VK token $configFile = __DIR__ . '/config.php'; $configContent = file_get_contents($configFile); // Check if vk section exists if (strpos($configContent, "'vk'") !== false) { // Update existing vk access_token $configContent = preg_replace( "/('vk'\s*=>\s*\[\s*'access_token'\s*=>\s*')[^']*(')/s", "$1" . addslashes($token) . "$2", $configContent ); } else { // Add vk section before the closing ]; $vkSection = "\n 'vk' => [\n 'access_token' => '" . addslashes($token) . "',\n ],\n"; $configContent = preg_replace("/(\];)\s*$/", $vkSection . "$1", $configContent); } if (file_put_contents($configFile, $configContent)) { // Validate the new token require_once __DIR__ . '/classes/VKAPI.php'; $vk = new VKAPI($token); $validation = $vk->validateToken(); echo json_encode([ 'success' => true, 'message' => 'Токен сохранён', 'validation' => $validation ]); } else { echo json_encode(['error' => 'Не удалось сохранить config.php']); } break; case 'change_password': $currentPassword = $_POST['current_password'] ?? ''; $newPassword = $_POST['new_password'] ?? ''; if (strlen($newPassword) < 8) { echo json_encode(['error' => 'New password must be at least 8 characters']); exit; } $username = $auth->getCurrentUser(); if ($auth->changePassword($username, $currentPassword, $newPassword)) { echo json_encode(['success' => true, 'message' => 'Password changed successfully']); } else { echo json_encode(['error' => 'Current password is incorrect']); } break; // ============ DIGITAL BADGE (round display) ============ case 'get_badge_settings': $settingsFile = __DIR__ . '/data/badge_settings.json'; if (file_exists($settingsFile)) { $settings = json_decode(file_get_contents($settingsFile), true) ?: []; echo json_encode(['success' => true, 'settings' => $settings]); } else { echo json_encode(['success' => true, 'settings' => ['nickname' => '']]); } break; case 'save_badge_settings': $dataDir = __DIR__ . '/data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } $settingsFile = $dataDir . '/badge_settings.json'; $settings = [ 'nickname' => trim((string)($_POST['nickname'] ?? '')), ]; if (file_put_contents($settingsFile, json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) { echo json_encode(['success' => true]); } else { echo json_encode(['error' => 'Не удалось сохранить настройки']); } break; case 'badge_save': // Save generated badge PNG (sent as base64 data URL) to /data/badges/. $dataUrl = $_POST['image'] ?? ''; if (strpos($dataUrl, 'data:image/png;base64,') !== 0) { echo json_encode(['error' => 'Некорректный формат изображения']); exit; } $base64 = substr($dataUrl, strlen('data:image/png;base64,')); $binary = base64_decode($base64, true); if ($binary === false) { echo json_encode(['error' => 'Не удалось декодировать изображение']); exit; } if (strlen($binary) > 5 * 1024 * 1024) { echo json_encode(['error' => 'Файл слишком большой']); exit; } $badgesDir = __DIR__ . '/data/badges'; if (!is_dir($badgesDir)) { mkdir($badgesDir, 0755, true); } $filename = 'badge_' . date('Ymd_His') . '_' . substr(bin2hex(random_bytes(4)), 0, 8) . '.png'; $filepath = $badgesDir . '/' . $filename; if (file_put_contents($filepath, $binary) === false) { echo json_encode(['error' => 'Не удалось сохранить файл']); exit; } $indexFile = $badgesDir . '/index.json'; $index = []; if (file_exists($indexFile)) { $index = json_decode(file_get_contents($indexFile), true) ?: []; } array_unshift($index, [ 'filename' => $filename, 'created_at' => date('Y-m-d H:i:s'), 'has_price' => !empty($_POST['has_price']), 'has_nickname' => !empty($_POST['has_nickname']), 'label' => trim((string)($_POST['label'] ?? '')), ]); // Keep at most 100 entries $index = array_slice($index, 0, 100); file_put_contents($indexFile, json_encode($index, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $path = dirname($_SERVER['REQUEST_URI']); $url = $protocol . '://' . $host . $path . '/data/badges/' . $filename; echo json_encode(['success' => true, 'filename' => $filename, 'url' => $url]); break; case 'badge_list': $indexFile = __DIR__ . '/data/badges/index.json'; if (!file_exists($indexFile)) { echo json_encode(['success' => true, 'items' => []]); break; } $index = json_decode(file_get_contents($indexFile), true) ?: []; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $path = dirname($_SERVER['REQUEST_URI']); $items = []; foreach ($index as $entry) { $fname = $entry['filename'] ?? ''; if (!$fname) continue; $absPath = __DIR__ . '/data/badges/' . $fname; if (!file_exists($absPath)) continue; $items[] = [ 'filename' => $fname, 'created_at' => $entry['created_at'] ?? '', 'has_price' => !empty($entry['has_price']), 'has_nickname' => !empty($entry['has_nickname']), 'label' => $entry['label'] ?? '', 'url' => $protocol . '://' . $host . $path . '/data/badges/' . $fname, ]; } echo json_encode(['success' => true, 'items' => $items]); break; case 'badge_delete': $filename = basename($_POST['filename'] ?? ''); if (!$filename || strpos($filename, 'badge_') !== 0) { echo json_encode(['error' => 'Некорректное имя файла']); exit; } $badgesDir = __DIR__ . '/data/badges'; $filepath = $badgesDir . '/' . $filename; if (file_exists($filepath)) { @unlink($filepath); } $indexFile = $badgesDir . '/index.json'; if (file_exists($indexFile)) { $index = json_decode(file_get_contents($indexFile), true) ?: []; $index = array_values(array_filter($index, function($e) use ($filename) { return ($e['filename'] ?? '') !== $filename; })); file_put_contents($indexFile, json_encode($index, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); } echo json_encode(['success' => true]); break; default: echo json_encode(['error' => 'Unknown action']); } } catch (Exception $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()]); }