"Vendor folder not found. Please run 'composer install'."]); exit; } require_once $autoloadPath; use Dompdf\Dompdf; use Dompdf\Options; use Dotenv\Dotenv; // Path to the .env file located outside the document root for security // Assuming script is in /home/user/htdocs/domain.com/cv/server/ // and .env is in /home/user/ $envPath = realpath(__DIR__ . '/../../../..'); if ($envPath && file_exists($envPath . '/.env')) { $dotenv = Dotenv::createImmutable($envPath); $dotenv->load(); } $rawData = file_get_contents('php://input'); $data = json_decode($rawData, true); $action = $data['action'] ?? 'generateText'; // Prioritize API key from .env over frontend payload $apiKey = $_ENV['GEMINI_API_KEY'] ?? getenv('GEMINI_API_KEY') ?: ($data['apiKey'] ?? ''); if (empty($apiKey)) { http_response_code(400); echo json_encode(["error" => "Missing apiKey. Please set GEMINI_API_KEY in .env or pass it in request."]); exit; } $model = "gemini-flash-lite-latest"; // $model = "gemini-2.5-flash"; $geminiUrl = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key=" . $apiKey; // ========================================== // ACTION 1: Generate ATS PDF CV // ========================================== if ($action === 'generatePdf') { $jobDescription = $data['jobDescription'] ?? ''; $prompt = "You are an expert ATS CV tailor. Read the following job description and generate tailored content for my CV to maximize my chances of getting an interview. Return ONLY a valid JSON object with EXACTLY three keys: 'headline', 'summary', and 'skills'. The 'headline' should be a 5-6 word professional title relevant to the job. The 'summary' should be a 3-sentence powerful paragraph highlighting skills relevant to the job. The 'skills' should be a comma-separated list of 10 highly relevant ATS keywords. Do NOT use markdown blocks like ```json, just return raw JSON text. Job Description: " . substr($jobDescription, 0, 4000); $payload = json_encode([ "contents" => [["parts" => [["text" => $prompt]]]], "generationConfig" => ["temperature" => 0.2, "responseMimeType" => "application/json"] ]); $ch = curl_init($geminiUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { http_response_code(500); echo json_encode(["error" => "Gemini API Error", "details" => json_decode($response)]); exit; } $responseData = json_decode($response, true); $aiText = $responseData['candidates'][0]['content']['parts'][0]['text'] ?? '{}'; $aiText = str_replace(['```json', '```'], '', $aiText); $parsedJson = json_decode(trim($aiText), true); $headline = $parsedJson['headline'] ?? "Solutions Architect & Technical Leader"; $summary = $parsedJson['summary'] ?? "Experienced professional with a strong background in software engineering."; $skills = $parsedJson['skills'] ?? "Architecture, APIs, Cloud, Backend Systems, System Design"; $templatePath = __DIR__ . '/cv_template.html'; $html = file_get_contents($templatePath); $html = str_replace('{{JOB_HEADLINE}}', htmlspecialchars($headline), $html); $html = str_replace('{{TAILORED_SUMMARY}}', htmlspecialchars($summary), $html); $html = str_replace('{{DYNAMIC_SKILLS}}', htmlspecialchars($skills), $html); try { $options = new Options(); $options->set('isHtml5ParserEnabled', true); $options->set('defaultFont', 'Helvetica'); $dompdf = new Dompdf($options); $dompdf->loadHtml($html); $dompdf->setPaper('A4', 'portrait'); $dompdf->render(); $pdfOutput = $dompdf->output(); $rawJobTitle = $data['jobTitle'] ?? 'Job'; $safeJobTitle = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $rawJobTitle); $safeJobTitle = trim($safeJobTitle, '_'); $fileName = "Hamza_Ayed - {$safeJobTitle}.pdf"; echo json_encode([ "success" => true, "pdf" => base64_encode($pdfOutput), "filename" => $fileName ]); } catch (Exception $e) { http_response_code(500); echo json_encode(["error" => "PDF Generation Failed", "details" => $e->getMessage()]); } exit; } // ========================================== // ACTION 2: Standard Proxy (Text generation) // ========================================== if ($action === 'generateText') { $prompt = $data['prompt'] ?? ''; $payload = json_encode([ "contents" => [["parts" => [["text" => $prompt]]]], "generationConfig" => ["temperature" => 0.7, "maxOutputTokens" => 8192] ]); $ch = curl_init($geminiUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { http_response_code($httpCode); echo $response; exit; } // Pass the exact Gemini response back to the extension echo $response; exit; }