Update Saqel Platform: 2026-09-08 23:35:48
This commit is contained in:
@@ -249,7 +249,7 @@
|
|||||||
|
|
||||||
- **محلياً**: كتابة الكود + `php artisan test` (SQLite in-memory) — لا حاجة Docker.
|
- **محلياً**: كتابة الكود + `php artisan test` (SQLite in-memory) — لا حاجة Docker.
|
||||||
- **السيرفر**: `docker compose up -d --build` يشغّل: php-fpm، nginx، mysql:8، queue worker (لتشغيل مهام AI في الخلفية).
|
- **السيرفر**: `docker compose up -d --build` يشغّل: php-fpm، nginx، mysql:8، queue worker (لتشغيل مهام AI في الخلفية).
|
||||||
- أسرار الإنتاج (GEMINI_API_KEY، كلمات مرور DB) عبر `.env` على السيرفر فقط — لا تُرفع للمستودع.
|
- أسرار الإنتاج (GEMINI_KEY، كلمات مرور DB) عبر `.env` على السيرفر فقط — لا تُرفع للمستودع.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ ALLOWED_ORIGIN=https://saqel.intaleqapp.com
|
|||||||
SAQEL_AI_NODES_JSON=[]
|
SAQEL_AI_NODES_JSON=[]
|
||||||
|
|
||||||
# Gemini AI API Configuration
|
# Gemini AI API Configuration
|
||||||
GEMINI_API_KEY=your_gemini_api_key_here
|
GEMINI_KEY=your_gemini_api_key_here
|
||||||
GEMINI_MODEL=gemini-2.0-flash
|
GEMINI_MODEL=gemini-2.0-flash
|
||||||
|
|
||||||
# Cloudflare R2 Object Storage & CDN
|
# Cloudflare R2 Object Storage & CDN
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class AiInteractiveLabGeneratorService
|
|||||||
|
|
||||||
$apiKey = self::resolveGeminiKey();
|
$apiKey = self::resolveGeminiKey();
|
||||||
if (!$apiKey) {
|
if (!$apiKey) {
|
||||||
throw new \RuntimeException("مفتاح Gemini API غير مهيأ (GEMINI_API_KEY).");
|
throw new \RuntimeException("مفتاح Gemini API غير مهيأ (GEMINI_KEY).");
|
||||||
}
|
}
|
||||||
|
|
||||||
$prompt = self::buildSimulationPrompt($lessonContent, $subjectSlug, $simSlug);
|
$prompt = self::buildSimulationPrompt($lessonContent, $subjectSlug, $simSlug);
|
||||||
@@ -204,15 +204,15 @@ PROMPT;
|
|||||||
|
|
||||||
private static function resolveGeminiKey(): ?string
|
private static function resolveGeminiKey(): ?string
|
||||||
{
|
{
|
||||||
if (!empty($_ENV['GEMINI_API_KEY'])) return $_ENV['GEMINI_API_KEY'];
|
if (!empty($_ENV['GEMINI_KEY'])) return $_ENV['GEMINI_KEY'];
|
||||||
if (!empty(getenv('GEMINI_API_KEY'))) return getenv('GEMINI_API_KEY');
|
if (!empty(getenv('GEMINI_KEY'))) return getenv('GEMINI_KEY');
|
||||||
|
|
||||||
// Check backend/.env
|
// Check backend/.env
|
||||||
$envPath = __DIR__ . '/../../.env';
|
$envPath = __DIR__ . '/../../.env';
|
||||||
if (file_exists($envPath)) {
|
if (file_exists($envPath)) {
|
||||||
$lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
$lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
if (str_starts_with(trim($line), 'GEMINI_API_KEY=')) {
|
if (str_starts_with(trim($line), 'GEMINI_KEY=')) {
|
||||||
return trim(substr(trim($line), 15), '"\' ');
|
return trim(substr(trim($line), 15), '"\' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class AiLessonEnhancerService
|
|||||||
|
|
||||||
private static function callGemini(string $prompt): ?array
|
private static function callGemini(string $prompt): ?array
|
||||||
{
|
{
|
||||||
$apiKey = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
|
$apiKey = Env::get('GEMINI_KEY') ?: getenv('GEMINI_KEY');
|
||||||
if (!$apiKey) {
|
if (!$apiKey) {
|
||||||
$apiKey = Env::get('GOOGLE_API_KEY') ?: getenv('GOOGLE_API_KEY');
|
$apiKey = Env::get('GOOGLE_API_KEY') ?: getenv('GOOGLE_API_KEY');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ class AiQuestionBankGeneratorService
|
|||||||
*/
|
*/
|
||||||
private static function callGemini(string $prompt): ?array
|
private static function callGemini(string $prompt): ?array
|
||||||
{
|
{
|
||||||
$apiKey = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
|
$apiKey = Env::get('GEMINI_KEY') ?: getenv('GEMINI_KEY');
|
||||||
if (!$apiKey) {
|
if (!$apiKey) {
|
||||||
$apiKey = Env::get('GOOGLE_API_KEY') ?: getenv('GOOGLE_API_KEY');
|
$apiKey = Env::get('GOOGLE_API_KEY') ?: getenv('GOOGLE_API_KEY');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class AiVideoAnalyzerService
|
|||||||
*/
|
*/
|
||||||
private static function nextGeminiApiKey(): string
|
private static function nextGeminiApiKey(): string
|
||||||
{
|
{
|
||||||
$raw = trim((string)(getenv('GEMINI_API_KEYS') ?: getenv('GEMINI_API_KEY') ?: ''));
|
$raw = trim((string)(getenv('GEMINI_API_KEYS') ?: getenv('GEMINI_KEY') ?: ''));
|
||||||
$keys = array_values(array_filter(array_map('trim', explode(',', $raw))));
|
$keys = array_values(array_filter(array_map('trim', explode(',', $raw))));
|
||||||
if (empty($keys)) {
|
if (empty($keys)) {
|
||||||
return '';
|
return '';
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class AiVideoGeneratorService
|
|||||||
|
|
||||||
private static function callGemini(string $prompt): ?array
|
private static function callGemini(string $prompt): ?array
|
||||||
{
|
{
|
||||||
$apiKey = Env::get('GEMINI_API_KEY');
|
$apiKey = Env::get('GEMINI_KEY');
|
||||||
if (!$apiKey) return null;
|
if (!$apiKey) return null;
|
||||||
|
|
||||||
$model = Env::get('GEMINI_MODEL') ?: 'gemini-flash-lite-latest';
|
$model = Env::get('GEMINI_MODEL') ?: 'gemini-flash-lite-latest';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class CurriculumExtractorService
|
|||||||
$fileNameClean = trim(preg_replace('/\.[^.]+$/u', '', $fileName));
|
$fileNameClean = trim(preg_replace('/\.[^.]+$/u', '', $fileName));
|
||||||
|
|
||||||
// Try AI Structuring with Gemini 2.0 Flash
|
// Try AI Structuring with Gemini 2.0 Flash
|
||||||
$geminiKey = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
|
$geminiKey = Env::get('GEMINI_KEY') ?: getenv('GEMINI_KEY');
|
||||||
if (!empty($geminiKey) && !empty($rawText)) {
|
if (!empty($geminiKey) && !empty($rawText)) {
|
||||||
$aiStructure = self::callGeminiStructurer($fileName, $rawText, $geminiKey);
|
$aiStructure = self::callGeminiStructurer($fileName, $rawText, $geminiKey);
|
||||||
if (!empty($aiStructure['units']) && !empty($aiStructure['subject_name'])) {
|
if (!empty($aiStructure['units']) && !empty($aiStructure['subject_name'])) {
|
||||||
|
|||||||
@@ -40,15 +40,15 @@ try {
|
|||||||
// Support either GEMINI_API_KEYS=key1,key2 or the legacy singular
|
// Support either GEMINI_API_KEYS=key1,key2 or the legacy singular
|
||||||
// variable containing a comma-separated pool. Legacy services receive
|
// variable containing a comma-separated pool. Legacy services receive
|
||||||
// one safe key; services with round-robin support consume the pool.
|
// one safe key; services with round-robin support consume the pool.
|
||||||
$geminiPool = trim((string)(getenv('GEMINI_API_KEYS') ?: getenv('GEMINI_API_KEY') ?: ''));
|
$geminiPool = trim((string)(getenv('GEMINI_API_KEYS') ?: getenv('GEMINI_KEY') ?: ''));
|
||||||
$geminiKeys = array_values(array_filter(array_map('trim', explode(',', $geminiPool))));
|
$geminiKeys = array_values(array_filter(array_map('trim', explode(',', $geminiPool))));
|
||||||
if (!empty($geminiKeys)) {
|
if (!empty($geminiKeys)) {
|
||||||
putenv('GEMINI_API_KEYS=' . implode(',', $geminiKeys));
|
putenv('GEMINI_API_KEYS=' . implode(',', $geminiKeys));
|
||||||
$_ENV['GEMINI_API_KEYS'] = implode(',', $geminiKeys);
|
$_ENV['GEMINI_API_KEYS'] = implode(',', $geminiKeys);
|
||||||
$_SERVER['GEMINI_API_KEYS'] = implode(',', $geminiKeys);
|
$_SERVER['GEMINI_API_KEYS'] = implode(',', $geminiKeys);
|
||||||
putenv('GEMINI_API_KEY=' . $geminiKeys[0]);
|
putenv('GEMINI_KEY=' . $geminiKeys[0]);
|
||||||
$_ENV['GEMINI_API_KEY'] = $geminiKeys[0];
|
$_ENV['GEMINI_KEY'] = $geminiKeys[0];
|
||||||
$_SERVER['GEMINI_API_KEY'] = $geminiKeys[0];
|
$_SERVER['GEMINI_KEY'] = $geminiKeys[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,11 +51,11 @@ $state = json_decode(file_get_contents($stateFile), true);
|
|||||||
$origName = $state['orig_name'] ?? 'Unknown.pdf';
|
$origName = $state['orig_name'] ?? 'Unknown.pdf';
|
||||||
|
|
||||||
Env::load(__DIR__ . '/../.env');
|
Env::load(__DIR__ . '/../.env');
|
||||||
$rawKeys = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
|
$rawKeys = Env::get('GEMINI_KEY') ?: getenv('GEMINI_KEY');
|
||||||
$apiKeys = array_values(array_filter(array_map('trim', explode(',', $rawKeys))));
|
$apiKeys = array_values(array_filter(array_map('trim', explode(',', $rawKeys))));
|
||||||
|
|
||||||
if (empty($apiKeys)) {
|
if (empty($apiKeys)) {
|
||||||
updateState($stateFile, 'error', 0, '⚠️ لم يتم العثور على مفتاح الذكاء الاصطناعي (GEMINI_API_KEY) في ملف .env.');
|
updateState($stateFile, 'error', 0, '⚠️ لم يتم العثور على مفتاح الذكاء الاصطناعي (GEMINI_KEY) في ملف .env.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ $maskedKey = substr($geminiKey, 0, 6) . '...' . substr($geminiKey, -4);
|
|||||||
updateState($stateFile, 'uploading_to_ai', 15, "تم تفعيل Round Robin. جاري استخدام المفتاح: {$maskedKey}");
|
updateState($stateFile, 'uploading_to_ai', 15, "تم تفعيل Round Robin. جاري استخدام المفتاح: {$maskedKey}");
|
||||||
|
|
||||||
if (empty($geminiKey)) {
|
if (empty($geminiKey)) {
|
||||||
updateState($stateFile, 'error', 0, '⚠️ لم يتم العثور على مفتاح الذكاء الاصطناعي (GEMINI_API_KEY) في ملف .env. النظام يحتاج إلى الـ AI لقراءة وتفريغ الكتب (بما فيها الكتب المصورة والرياضيات) بشكل حقيقي.');
|
updateState($stateFile, 'error', 0, '⚠️ لم يتم العثور على مفتاح الذكاء الاصطناعي (GEMINI_KEY) في ملف .env. النظام يحتاج إلى الـ AI لقراءة وتفريغ الكتب (بما فيها الكتب المصورة والرياضيات) بشكل حقيقي.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ if (file_exists($envFile)) {
|
|||||||
putenv(trim($name) . '=' . trim($value));
|
putenv(trim($name) . '=' . trim($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$apiKey = getenv('GEMINI_API_KEY');
|
$apiKey = getenv('GEMINI_KEY');
|
||||||
$keys = explode(',', $apiKey);
|
$keys = explode(',', $apiKey);
|
||||||
$apiKey = trim($keys[0]);
|
$apiKey = trim($keys[0]);
|
||||||
$url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key={$apiKey}";
|
$url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key={$apiKey}";
|
||||||
|
|||||||
Reference in New Issue
Block a user