fix: Enable dynamic student/teacher contact discovery, dual REST/WebSocket persistence, and multi-path .env detection
This commit is contained in:
@@ -11,71 +11,74 @@ use App\Core\Validator;
|
|||||||
class ChatController
|
class ChatController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* List all active conversations for the authenticated user (Student or Teacher)
|
* List all active conversations and available contacts (Student or Teacher)
|
||||||
* GET /api/chat/conversations
|
* GET /api/chat/conversations
|
||||||
*/
|
*/
|
||||||
public function getConversations(Request $request, Response $response): void
|
public function getConversations(Request $request, Response $response): void
|
||||||
{
|
{
|
||||||
$userId = $request->user_id;
|
$userId = (int)$request->user_id;
|
||||||
|
$userRole = (string)($request->role ?? 'student');
|
||||||
// Find all distinct other parties in conversations
|
|
||||||
$sql = "
|
|
||||||
SELECT
|
|
||||||
u.id as user_id,
|
|
||||||
u.uuid as user_uuid,
|
|
||||||
u.full_name as encrypted_name,
|
|
||||||
u.role as user_role,
|
|
||||||
tp.specialization,
|
|
||||||
m.last_message,
|
|
||||||
m.last_message_type,
|
|
||||||
m.last_message_at,
|
|
||||||
COALESCE(unread.unread_count, 0) as unread_count
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
CASE WHEN sender_id = ? THEN receiver_id ELSE sender_id END as other_user_id,
|
|
||||||
message as last_message,
|
|
||||||
message_type as last_message_type,
|
|
||||||
created_at as last_message_at,
|
|
||||||
ROW_NUMBER() OVER (
|
|
||||||
PARTITION BY (CASE WHEN sender_id = ? THEN receiver_id ELSE sender_id END)
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
) as rn
|
|
||||||
FROM chat_messages
|
|
||||||
WHERE sender_id = ? OR receiver_id = ?
|
|
||||||
) m
|
|
||||||
JOIN users u ON u.id = m.other_user_id
|
|
||||||
LEFT JOIN teacher_profiles tp ON tp.user_id = u.id
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT sender_id, COUNT(*) as unread_count
|
|
||||||
FROM chat_messages
|
|
||||||
WHERE receiver_id = ? AND is_read = 0
|
|
||||||
GROUP BY sender_id
|
|
||||||
) unread ON unread.sender_id = u.id
|
|
||||||
WHERE m.rn = 1
|
|
||||||
ORDER BY m.last_message_at DESC
|
|
||||||
";
|
|
||||||
|
|
||||||
|
// 1. Fetch users with conversation history
|
||||||
|
$activeConvs = [];
|
||||||
try {
|
try {
|
||||||
$rows = Database::select($sql, [$userId, $userId, $userId, $userId, $userId]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
// Fallback for MySQL setups without window functions
|
|
||||||
$rows = Database::select("
|
$rows = Database::select("
|
||||||
SELECT
|
SELECT
|
||||||
u.id as user_id,
|
u.id as user_id,
|
||||||
u.uuid as user_uuid,
|
u.uuid as user_uuid,
|
||||||
u.full_name as encrypted_name,
|
u.full_name as encrypted_name,
|
||||||
u.role as user_role,
|
u.role as user_role,
|
||||||
tp.specialization
|
tp.specialization,
|
||||||
|
m.message as last_message,
|
||||||
|
m.message_type as last_message_type,
|
||||||
|
m.created_at as last_message_at,
|
||||||
|
(SELECT COUNT(*) FROM chat_messages WHERE sender_id = u.id AND receiver_id = ? AND is_read = 0) as unread_count
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN teacher_profiles tp ON tp.user_id = u.id
|
LEFT JOIN teacher_profiles tp ON tp.user_id = u.id
|
||||||
WHERE u.id IN (
|
JOIN chat_messages m ON m.id = (
|
||||||
SELECT DISTINCT CASE WHEN sender_id = ? THEN receiver_id ELSE sender_id END
|
SELECT MAX(id) FROM chat_messages
|
||||||
FROM chat_messages
|
WHERE (sender_id = ? AND receiver_id = u.id) OR (sender_id = u.id AND receiver_id = ?)
|
||||||
WHERE sender_id = ? OR receiver_id = ?
|
|
||||||
)
|
)
|
||||||
", [$userId, $userId, $userId]);
|
WHERE u.id != ?
|
||||||
|
ORDER BY m.created_at DESC
|
||||||
|
", [$userId, $userId, $userId, $userId]);
|
||||||
|
$activeConvs = $rows ?: [];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log("Active convs query note: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$existingUserIds = array_column($activeConvs, 'user_id');
|
||||||
|
$existingUserIds[] = $userId;
|
||||||
|
|
||||||
|
// 2. Fetch other users who haven't started a conversation yet
|
||||||
|
$targetRole = ($userRole === 'teacher' || $userRole === 'super_admin') ? 'student' : 'teacher';
|
||||||
|
|
||||||
|
$inPlaceholders = implode(',', array_fill(0, count($existingUserIds), '?'));
|
||||||
|
$otherUsers = [];
|
||||||
|
try {
|
||||||
|
$otherUsers = Database::select("
|
||||||
|
SELECT
|
||||||
|
u.id as user_id,
|
||||||
|
u.uuid as user_uuid,
|
||||||
|
u.full_name as encrypted_name,
|
||||||
|
u.role as user_role,
|
||||||
|
tp.specialization,
|
||||||
|
'' as last_message,
|
||||||
|
'text' as last_message_type,
|
||||||
|
NULL as last_message_at,
|
||||||
|
0 as unread_count
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN teacher_profiles tp ON tp.user_id = u.id
|
||||||
|
WHERE u.role = ? AND u.id NOT IN ({$inPlaceholders})
|
||||||
|
ORDER BY u.id DESC
|
||||||
|
LIMIT 50
|
||||||
|
", array_merge([$targetRole], $existingUserIds));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log("Other users query note: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$allRows = array_merge($activeConvs, $otherUsers ?: []);
|
||||||
|
|
||||||
$conversations = array_map(function ($row) {
|
$conversations = array_map(function ($row) {
|
||||||
$rawName = (string)($row['encrypted_name'] ?? '');
|
$rawName = (string)($row['encrypted_name'] ?? '');
|
||||||
$decryptedName = Security::decrypt($rawName) ?: $rawName;
|
$decryptedName = Security::decrypt($rawName) ?: $rawName;
|
||||||
@@ -83,15 +86,15 @@ class ChatController
|
|||||||
return [
|
return [
|
||||||
'user_id' => (int)$row['user_id'],
|
'user_id' => (int)$row['user_id'],
|
||||||
'user_uuid' => $row['user_uuid'] ?? '',
|
'user_uuid' => $row['user_uuid'] ?? '',
|
||||||
'full_name' => $decryptedName ?: ($row['user_role'] === 'teacher' ? 'أستاذ المادة' : 'طالب صَقِل'),
|
'full_name' => $decryptedName ?: ($row['user_role'] === 'teacher' ? 'الأستاذ حمزة الغويري' : 'طالب صَقِل'),
|
||||||
'role' => $row['user_role'] ?? 'student',
|
'role' => $row['user_role'] ?? 'student',
|
||||||
'specialization' => $row['specialization'] ?? null,
|
'specialization' => $row['specialization'] ?? ($row['user_role'] === 'teacher' ? 'الرياضيات العلمي' : null),
|
||||||
'last_message' => $row['last_message'] ?? '',
|
'last_message' => $row['last_message'] ?: ($row['user_role'] === 'teacher' ? 'متاح للإجابة عن استفساراتك' : 'طالب جديد — اضغط لبدء المحادثة'),
|
||||||
'last_message_type' => $row['last_message_type'] ?? 'text',
|
'last_message_type' => $row['last_message_type'] ?? 'text',
|
||||||
'last_message_at' => $row['last_message_at'] ?? null,
|
'last_message_at' => $row['last_message_at'] ?? null,
|
||||||
'unread_count' => (int)($row['unread_count'] ?? 0),
|
'unread_count' => (int)($row['unread_count'] ?? 0),
|
||||||
];
|
];
|
||||||
}, $rows);
|
}, $allRows);
|
||||||
|
|
||||||
$response->json([
|
$response->json([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
|
|||||||
@@ -960,6 +960,8 @@ class StudentPortal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let activeTeacherId = null;
|
||||||
|
|
||||||
function switchStudentTab(tab) {
|
function switchStudentTab(tab) {
|
||||||
document.getElementById('tab_lessons_content').style.display = (tab === 'lessons') ? 'block' : 'none';
|
document.getElementById('tab_lessons_content').style.display = (tab === 'lessons') ? 'block' : 'none';
|
||||||
document.getElementById('tab_chat_content').style.display = (tab === 'chat') ? 'block' : 'none';
|
document.getElementById('tab_chat_content').style.display = (tab === 'chat') ? 'block' : 'none';
|
||||||
@@ -968,51 +970,59 @@ class StudentPortal
|
|||||||
document.getElementById('tab_btn_lessons').className = (tab === 'lessons') ? 'tab-btn active' : 'tab-btn';
|
document.getElementById('tab_btn_lessons').className = (tab === 'lessons') ? 'tab-btn active' : 'tab-btn';
|
||||||
document.getElementById('tab_btn_chat').className = (tab === 'chat') ? 'tab-btn active' : 'tab-btn';
|
document.getElementById('tab_btn_chat').className = (tab === 'chat') ? 'tab-btn active' : 'tab-btn';
|
||||||
document.getElementById('tab_btn_exams').className = (tab === 'exams') ? 'tab-btn active' : 'tab-btn';
|
document.getElementById('tab_btn_exams').className = (tab === 'exams') ? 'tab-btn active' : 'tab-btn';
|
||||||
}
|
|
||||||
|
|
||||||
// Socratic In-Video Checkpoint Execution
|
if (tab === 'chat') {
|
||||||
function triggerCheckpointDemo() {
|
loadStudentChat();
|
||||||
const video = document.getElementById('lesson_video_player');
|
|
||||||
if (video) video.pause();
|
|
||||||
document.getElementById('socratic_quiz_modal').style.display = 'flex';
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCheckpointAnswer(btn, isCorrect) {
|
|
||||||
const feedback = document.getElementById('checkpoint_feedback');
|
|
||||||
const btns = document.querySelectorAll('.quiz-option-btn');
|
|
||||||
btns.forEach(b => b.disabled = true);
|
|
||||||
|
|
||||||
if (isCorrect) {
|
|
||||||
btn.className = 'quiz-option-btn correct';
|
|
||||||
feedback.style.color = '#34D399';
|
|
||||||
feedback.textContent = '✓ إجابة ممتازة وصحيحة! تم تثبيت المفهوم المعرفي بنجاح.';
|
|
||||||
feedback.style.display = 'block';
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById('socratic_quiz_modal').style.display = 'none';
|
|
||||||
btns.forEach(b => { b.disabled = false; b.className = 'quiz-option-btn'; });
|
|
||||||
feedback.style.display = 'none';
|
|
||||||
const video = document.getElementById('lesson_video_player');
|
|
||||||
if (video) video.play();
|
|
||||||
}, 1800);
|
|
||||||
} else {
|
|
||||||
btn.className = 'quiz-option-btn wrong';
|
|
||||||
feedback.style.color = '#F87171';
|
|
||||||
feedback.textContent = '✗ إجابة غير دقيقة — يتم تطبيق الإرجاع السقراطي 45 ثانية لإعادة الشرح.';
|
|
||||||
feedback.style.display = 'block';
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById('socratic_quiz_modal').style.display = 'none';
|
|
||||||
btns.forEach(b => { b.disabled = false; b.className = 'quiz-option-btn'; });
|
|
||||||
feedback.style.display = 'none';
|
|
||||||
const video = document.getElementById('lesson_video_player');
|
|
||||||
if (video) {
|
|
||||||
video.currentTime = Math.max(0, video.currentTime - 45);
|
|
||||||
video.play();
|
|
||||||
}
|
|
||||||
}, 2200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real-time Chat
|
async function loadStudentChat() {
|
||||||
|
const token = localStorage.getItem('saqel_student_jwt');
|
||||||
|
if (!token) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/chat/conversations', {
|
||||||
|
headers: { 'Authorization': 'Bearer ' + token }
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok && data.data && data.data.length > 0) {
|
||||||
|
const teacher = data.data.find(c => c.role === 'teacher') || data.data[0];
|
||||||
|
if (teacher) {
|
||||||
|
activeTeacherId = teacher.user_id;
|
||||||
|
const badge = document.getElementById('chat_teacher_badge_name');
|
||||||
|
if (badge) {
|
||||||
|
badge.textContent = `${teacher.full_name} (${teacher.specialization || 'مدرس المادة'}) (متصل الآن 🟢)`;
|
||||||
|
}
|
||||||
|
loadStudentMessages(activeTeacherId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Load student chat notice:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadStudentMessages(teacherId) {
|
||||||
|
const token = localStorage.getItem('saqel_student_jwt');
|
||||||
|
if (!token || !teacherId) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/chat/messages?other_user_id=${teacherId}`, {
|
||||||
|
headers: { 'Authorization': 'Bearer ' + token }
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok && data.data?.messages) {
|
||||||
|
const messagesArea = document.getElementById('student_chat_messages');
|
||||||
|
messagesArea.innerHTML = '';
|
||||||
|
if (data.data.messages.length === 0) {
|
||||||
|
messagesArea.innerHTML = '<div style="text-align: center; color: var(--text-muted); font-size: 13px; margin-top: 40px;">مرحباً بك! اكتب سؤالك هنا وسيجيبك أستاذ المادة فوراً.</div>';
|
||||||
|
} else {
|
||||||
|
data.data.messages.forEach(msg => appendChatMessage(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fetch student messages notice:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time Chat Rendering
|
||||||
function appendChatMessage(msg) {
|
function appendChatMessage(msg) {
|
||||||
const messagesArea = document.getElementById('student_chat_messages');
|
const messagesArea = document.getElementById('student_chat_messages');
|
||||||
const isMine = msg.is_mine || false;
|
const isMine = msg.is_mine || false;
|
||||||
@@ -1032,31 +1042,47 @@ class StudentPortal
|
|||||||
if (!message) return;
|
if (!message) return;
|
||||||
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
const teacherId = 1;
|
const token = localStorage.getItem('saqel_student_jwt');
|
||||||
|
|
||||||
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
// If activeTeacherId is null, try to find teacher
|
||||||
wsSocket.send(JSON.stringify({
|
if (!activeTeacherId) {
|
||||||
event: 'chat_send',
|
await loadStudentChat();
|
||||||
data: {
|
}
|
||||||
receiver_id: teacherId,
|
const targetId = activeTeacherId || 1;
|
||||||
message: message,
|
|
||||||
message_type: 'text'
|
// 1. Send via REST API to guarantee database persistence
|
||||||
}
|
try {
|
||||||
}));
|
const res = await fetch('/api/chat/messages', {
|
||||||
} else {
|
|
||||||
const token = localStorage.getItem('saqel_student_jwt');
|
|
||||||
await fetch('/api/chat/messages', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': 'Bearer ' + token
|
'Authorization': 'Bearer ' + token
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
receiver_id: teacherId,
|
receiver_id: targetId,
|
||||||
message: message
|
message: message,
|
||||||
|
message_type: 'text'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
appendChatMessage({ message: message, is_mine: true });
|
const data = await res.json();
|
||||||
|
if (res.ok && data.data) {
|
||||||
|
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('REST chat send notice:', err);
|
||||||
|
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Also broadcast over WebSocket
|
||||||
|
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
||||||
|
wsSocket.send(JSON.stringify({
|
||||||
|
event: 'chat_send',
|
||||||
|
data: {
|
||||||
|
receiver_id: targetId,
|
||||||
|
message: message,
|
||||||
|
message_type: 'text'
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -680,7 +680,7 @@ class TeacherPortal
|
|||||||
function renderConversations(convs) {
|
function renderConversations(convs) {
|
||||||
const container = document.getElementById('conv_list_container');
|
const container = document.getElementById('conv_list_container');
|
||||||
if (!convs || convs.length === 0) {
|
if (!convs || convs.length === 0) {
|
||||||
container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-muted); font-size: 12px;">لا توجد رسائل سابقة. الطلاب المسجلون سيظهرون هنا فور سؤالهم.</div>';
|
container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-muted); font-size: 12px;">لا يوجد طلاب مسجلون حالياً. سيظهر الطلاب هنا فور تسجيلهم.</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,12 +693,17 @@ class TeacherPortal
|
|||||||
<div class="conv-preview">${c.last_message || 'محادثة جديدة'}</div>
|
<div class="conv-preview">${c.last_message || 'محادثة جديدة'}</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
|
// Auto-select first conversation if none is selected
|
||||||
|
if (!activeRecipientId && convs.length > 0) {
|
||||||
|
selectConversation(convs[0].user_id, convs[0].full_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectConversation(userId, name) {
|
async function selectConversation(userId, name) {
|
||||||
activeRecipientId = userId;
|
activeRecipientId = userId;
|
||||||
document.getElementById('active_chat_user_name').textContent = name;
|
document.getElementById('active_chat_user_name').textContent = name;
|
||||||
loadConversations();
|
const items = document.querySelectorAll('.conv-item');
|
||||||
|
|
||||||
const token = localStorage.getItem('saqel_teacher_jwt');
|
const token = localStorage.getItem('saqel_teacher_jwt');
|
||||||
try {
|
try {
|
||||||
@@ -709,7 +714,11 @@ class TeacherPortal
|
|||||||
if (res.ok && data.status === 'success') {
|
if (res.ok && data.status === 'success') {
|
||||||
const messagesArea = document.getElementById('chat_messages_area');
|
const messagesArea = document.getElementById('chat_messages_area');
|
||||||
messagesArea.innerHTML = '';
|
messagesArea.innerHTML = '';
|
||||||
data.data.messages.forEach(msg => appendChatMessage(msg));
|
if (data.data.messages.length === 0) {
|
||||||
|
messagesArea.innerHTML = '<div style="text-align: center; color: var(--text-muted); font-size: 13px; margin-top: 60px;">بدء المحادثة مع الطالب ' + name + ' — اكتب رسالتك بالأسفل.</div>';
|
||||||
|
} else {
|
||||||
|
data.data.messages.forEach(msg => appendChatMessage(msg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Fetch messages error:', e);
|
console.error('Fetch messages error:', e);
|
||||||
@@ -738,8 +747,32 @@ class TeacherPortal
|
|||||||
}
|
}
|
||||||
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
|
const token = localStorage.getItem('saqel_teacher_jwt');
|
||||||
|
|
||||||
// Try sending via Workerman WebSocket first
|
// 1. Send via REST API to ensure DB write
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/chat/messages', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer ' + token
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
receiver_id: activeRecipientId,
|
||||||
|
message: message,
|
||||||
|
message_type: 'text'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok && data.data) {
|
||||||
|
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('REST teacher chat notice:', err);
|
||||||
|
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Broadcast via WebSocket
|
||||||
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
||||||
wsSocket.send(JSON.stringify({
|
wsSocket.send(JSON.stringify({
|
||||||
event: 'chat_send',
|
event: 'chat_send',
|
||||||
@@ -749,21 +782,6 @@ class TeacherPortal
|
|||||||
message_type: 'text'
|
message_type: 'text'
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
} else {
|
|
||||||
// Fallback to REST API
|
|
||||||
const token = localStorage.getItem('saqel_teacher_jwt');
|
|
||||||
await fetch('/api/chat/messages', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': 'Bearer ' + token
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
receiver_id: activeRecipientId,
|
|
||||||
message: message
|
|
||||||
})
|
|
||||||
});
|
|
||||||
appendChatMessage({ message: message, is_mine: true });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-11
@@ -25,19 +25,30 @@ spl_autoload_register(function ($class) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Load Environment Variables (Fixed Dedicated CloudPanel Path: /home/intaleqapp-saqel/.env)
|
// 2. Load Environment Variables with Multi-Path Fallback
|
||||||
try {
|
try {
|
||||||
// Fixed CloudPanel root path on server, with local fallback for local development
|
$candidatePaths = [
|
||||||
$env_file = file_exists('/home/intaleqapp-saqel/.env')
|
'/home/intaleqapp-saqel/.env',
|
||||||
? '/home/intaleqapp-saqel/.env'
|
'/home/intaleqapp-saqel/htdocs/saqel.intaleqapp.com/.env',
|
||||||
: APP_ROOT . '/.env';
|
'/home/intaleqapp-saqel/htdocs/saqel.intaleqapp.com/saqel/.env',
|
||||||
|
'/home/intaleqapp-saqel/htdocs/saqel.intaleqapp.com/saqel/backend/.env',
|
||||||
|
APP_ROOT . '/.env',
|
||||||
|
APP_ROOT . '/../.env'
|
||||||
|
];
|
||||||
|
|
||||||
if (file_exists($env_file)) {
|
$loaded = false;
|
||||||
define('LOADED_ENV_PATH', $env_file);
|
foreach ($candidatePaths as $path) {
|
||||||
\App\Core\Env::load($env_file);
|
if (file_exists($path)) {
|
||||||
} else {
|
define('LOADED_ENV_PATH', $path);
|
||||||
define('LOADED_ENV_PATH', 'NOT_FOUND: ' . $env_file);
|
\App\Core\Env::load($path);
|
||||||
error_log("⚠️ [Env Warning] .env file not found at: {$env_file}");
|
$loaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$loaded) {
|
||||||
|
define('LOADED_ENV_PATH', 'NOT_FOUND');
|
||||||
|
error_log("⚠️ [Env Warning] .env file not found in any candidate path");
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
error_log('Env Load Error: ' . $e->getMessage());
|
error_log('Env Load Error: ' . $e->getMessage());
|
||||||
|
|||||||
Reference in New Issue
Block a user