314 lines
13 KiB
PHP
314 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Core\Request;
|
|
use App\Core\Response;
|
|
use App\Core\Database;
|
|
use App\Core\Security;
|
|
use App\Models\CompanySubscription;
|
|
|
|
class SuperAdminController extends BaseController
|
|
{
|
|
/**
|
|
* Helper to verify if the requester is the Super Admin (Company ID 1 and role Admin)
|
|
*/
|
|
private function verifySuperAdmin(Request $request, Response $response): bool
|
|
{
|
|
if ((int)$request->company_id !== 1 || $request->role !== 'admin') {
|
|
$response->status(403)->json(['error' => 'Forbidden: Super Admin privileges required.']);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get platform statistics and companies list
|
|
* GET /api/admin/stats
|
|
*/
|
|
public function getStats(Request $request, Response $response): void
|
|
{
|
|
if (!$this->verifySuperAdmin($request, $response)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Overall stats
|
|
$companiesCount = Database::selectOne("SELECT COUNT(*) as count FROM companies")['count'] ?? 0;
|
|
$sessionsCount = Database::selectOne("SELECT COUNT(*) as count FROM whatsapp_sessions")['count'] ?? 0;
|
|
$connectedSessions = Database::selectOne("SELECT COUNT(*) as count FROM whatsapp_sessions WHERE status = 'connected'")['count'] ?? 0;
|
|
|
|
// Detailed list of all companies and their current subscriptions
|
|
$companies = Database::select("
|
|
SELECT
|
|
c.id,
|
|
c.name,
|
|
c.status as company_status,
|
|
c.created_at,
|
|
cs.plan_id,
|
|
cs.status as sub_status,
|
|
cs.starts_at,
|
|
cs.ends_at,
|
|
cs.payment_method,
|
|
cs.receipt_reference,
|
|
p.name as plan_name,
|
|
(SELECT COUNT(*) FROM whatsapp_sessions WHERE company_id = c.id) as sessions_count,
|
|
(SELECT COUNT(*) FROM whatsapp_sessions WHERE company_id = c.id AND status = 'connected') as active_sessions,
|
|
COALESCE(cu.request_count, 0) as request_usage,
|
|
COALESCE(cu.voice_count, 0) as voice_usage,
|
|
COALESCE(cu.ocr_count, 0) as ocr_usage
|
|
FROM companies c
|
|
LEFT JOIN company_subscriptions cs ON c.id = cs.company_id AND cs.status IN ('active', 'trialing', 'pending_approval')
|
|
LEFT JOIN subscription_plans p ON cs.plan_id = p.id
|
|
LEFT JOIN company_subscription_usage cu ON cu.company_id = c.id
|
|
AND cu.billing_start <= CURRENT_DATE()
|
|
AND cu.billing_end >= CURRENT_DATE()
|
|
ORDER BY c.created_at DESC
|
|
");
|
|
|
|
$pendingApprovals = [];
|
|
$activeCompanies = [];
|
|
|
|
foreach ($companies as $c) {
|
|
if ($c['sub_status'] === 'pending_approval') {
|
|
$pendingApprovals[] = $c;
|
|
} else {
|
|
$activeCompanies[] = $c;
|
|
}
|
|
}
|
|
|
|
// 4. Fetch all available plans to allow admin to upgrade them manually
|
|
$plans = Database::select("SELECT id, name, price FROM subscription_plans ORDER BY price ASC");
|
|
|
|
$response->json([
|
|
'status' => 'success',
|
|
'data' => [
|
|
'stats' => [
|
|
'total_companies' => (int)$companiesCount,
|
|
'total_sessions' => (int)$sessionsCount,
|
|
'connected_sessions' => (int)$connectedSessions
|
|
],
|
|
'companies' => $activeCompanies,
|
|
'pending_approvals' => $pendingApprovals,
|
|
'plans' => $plans
|
|
]
|
|
]);
|
|
|
|
} catch (\Exception $e) {
|
|
error_log("[SuperAdminController Error] " . $e->getMessage());
|
|
$response->status(500)->json(['error' => 'Failed to fetch platform stats: ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subscribe or upgrade a company to a plan
|
|
* POST /api/admin/companies/subscribe
|
|
*/
|
|
public function subscribeCompany(Request $request, Response $response): void
|
|
{
|
|
if (!$this->verifySuperAdmin($request, $response)) {
|
|
return;
|
|
}
|
|
|
|
$body = $request->getBody();
|
|
$targetCompanyId = isset($body['company_id']) ? (int)$body['company_id'] : null;
|
|
$planId = isset($body['plan_id']) ? (int)$body['plan_id'] : null;
|
|
$durationDays = isset($body['duration_days']) ? (int)$body['duration_days'] : 30;
|
|
|
|
if (!$targetCompanyId || !$planId) {
|
|
$response->status(400)->json(['error' => 'Missing company_id or plan_id']);
|
|
return;
|
|
}
|
|
|
|
// Verify company exists
|
|
$companyExists = Database::selectOne("SELECT id FROM companies WHERE id = ?", [$targetCompanyId]);
|
|
if (!$companyExists) {
|
|
$response->status(404)->json(['error' => 'Company not found']);
|
|
return;
|
|
}
|
|
|
|
// Verify plan exists
|
|
$planExists = Database::selectOne("SELECT id FROM subscription_plans WHERE id = ?", [$planId]);
|
|
if (!$planExists) {
|
|
$response->status(404)->json(['error' => 'Subscription plan not found']);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Subscribe the company
|
|
$subId = CompanySubscription::subscribeCompany($targetCompanyId, $planId, $durationDays, 'manual_admin', 'admin_' . $request->user_id);
|
|
|
|
// Clean active subscription cache for the company
|
|
if (class_exists('App\Core\Cache')) {
|
|
\App\Core\Cache::delete("company_subscription:{$targetCompanyId}");
|
|
\App\Core\Cache::delete("company_subscription_{$targetCompanyId}");
|
|
}
|
|
|
|
$response->json([
|
|
'status' => 'success',
|
|
'message' => 'Subscription updated successfully',
|
|
'subscription_id' => $subId
|
|
]);
|
|
} catch (\Exception $e) {
|
|
error_log("[SuperAdminController Error] " . $e->getMessage());
|
|
$response->status(500)->json(['error' => 'Failed to update subscription: ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Approve a pending billing request
|
|
* POST /api/admin/companies/approve-billing
|
|
*/
|
|
public function approveBilling(Request $request, Response $response): void
|
|
{
|
|
$body = $request->getBody();
|
|
$targetCompanyId = isset($body['company_id']) ? (int)$body['company_id'] : null;
|
|
|
|
if (!$targetCompanyId) {
|
|
$response->status(400)->json(['error' => 'Missing company_id']);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Find the pending subscription
|
|
$pending = Database::selectOne("SELECT * FROM company_subscriptions WHERE company_id = ? AND status = 'pending_approval' ORDER BY id DESC LIMIT 1", [$targetCompanyId]);
|
|
if (!$pending) {
|
|
$response->status(404)->json(['error' => 'No pending approval found for this company']);
|
|
return;
|
|
}
|
|
|
|
// Deactivate other active/trialing subscriptions
|
|
Database::execute("UPDATE company_subscriptions SET status = 'expired' WHERE company_id = ? AND status IN ('active', 'trialing')", [$targetCompanyId]);
|
|
|
|
// Mark the pending one as active
|
|
Database::execute("UPDATE company_subscriptions SET status = 'active' WHERE id = ?", [$pending['id']]);
|
|
|
|
// Clear active subscription cache for the company
|
|
if (class_exists('App\Core\Cache')) {
|
|
\App\Core\Cache::delete("company_subscription_{$targetCompanyId}");
|
|
}
|
|
|
|
$response->json([
|
|
'status' => 'success',
|
|
'message' => 'Billing approved and subscription activated successfully'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$response->status(500)->json(['error' => 'Failed to approve billing: ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export WhatsApp chats to a public text file
|
|
* POST /api/admin/export-chats
|
|
*/
|
|
public function exportChats(Request $request, Response $response): void
|
|
{
|
|
if (!$this->verifySuperAdmin($request, $response)) {
|
|
return;
|
|
}
|
|
|
|
// Get the WhatsApp session for the Super Admin company (ID 1)
|
|
$session = \App\Models\WhatsAppSession::findByCompany(1);
|
|
if (!$session) {
|
|
$response->status(404)->json(['error' => 'Super Admin WhatsApp session not found']);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$sessionId = $session['id'];
|
|
|
|
// 1. Fetch all logged messages for this session
|
|
$messages = Database::select(
|
|
"SELECT * FROM messages_log WHERE session_id = ? ORDER BY id ASC",
|
|
[$sessionId]
|
|
);
|
|
|
|
// 2. Fetch all contacts for Company 1 to map phone numbers to names
|
|
$contactsList = Database::select(
|
|
"SELECT name, phone FROM contacts WHERE company_id = 1"
|
|
);
|
|
$contactNames = [];
|
|
foreach ($contactsList as $c) {
|
|
try {
|
|
$decryptedPhone = Security::decrypt($c['phone']);
|
|
$contactNames[$decryptedPhone] = $c['name'];
|
|
} catch (\Exception $e) {
|
|
// Skip decryption failure for this specific contact
|
|
}
|
|
}
|
|
|
|
// 3. Construct the formatted chat log
|
|
$outputText = "==================================================\n";
|
|
$outputText .= "سجل محادثات منصة نبيه - جلسة: " . $session['session_key'] . "\n";
|
|
$outputText .= "تاريخ التصدير: " . date('Y-m-d H:i:s') . "\n";
|
|
$outputText .= "==================================================\n\n";
|
|
|
|
if (empty($messages)) {
|
|
$outputText .= "لا يوجد سجل رسائل متاح في قاعدة البيانات لهذه الجلسة.\n";
|
|
} else {
|
|
// Group messages by decrypted contact phone number
|
|
$chats = [];
|
|
foreach ($messages as $msg) {
|
|
try {
|
|
$phone = Security::decrypt($msg['contact_phone']);
|
|
} catch (\Exception $e) {
|
|
$phone = 'unknown_' . ($msg['contact_phone_hash'] ?? $msg['id']);
|
|
}
|
|
if (!isset($chats[$phone])) {
|
|
$chats[$phone] = [];
|
|
}
|
|
$chats[$phone][] = $msg;
|
|
}
|
|
|
|
// Format each chat group
|
|
foreach ($chats as $phone => $chatMsgs) {
|
|
$name = isset($contactNames[$phone]) ? $contactNames[$phone] : 'عميل غير مسمى';
|
|
$outputText .= "--------------------------------------------------\n";
|
|
$outputText .= "المحادثة مع: {$name} ({$phone})\n";
|
|
$outputText .= "--------------------------------------------------\n";
|
|
|
|
foreach ($chatMsgs as $msg) {
|
|
$fromMe = $msg['direction'] === 'outbound';
|
|
$sender = $fromMe ? 'المنصة (نبيه)' : $name;
|
|
|
|
try {
|
|
$body = Security::decrypt($msg['message_body']);
|
|
} catch (\Exception $e) {
|
|
$body = '[خطأ في فك تشفير الرسالة]';
|
|
}
|
|
|
|
$dateStr = $msg['created_at'];
|
|
|
|
if ($msg['message_type'] === 'text') {
|
|
$outputText .= "[{$dateStr}] {$sender}: {$body}\n";
|
|
} else if ($msg['message_type'] === 'audio') {
|
|
$outputText .= "[{$dateStr}] {$sender}: [رسالة صوتية] " . ($body ? "- {$body}" : "") . "\n";
|
|
} else if ($msg['message_type'] === 'image') {
|
|
$outputText .= "[{$dateStr}] {$sender}: [صورة] " . ($body ? "- {$body}" : "") . "\n";
|
|
} else {
|
|
$outputText .= "[{$dateStr}] {$sender}: [" . $msg['message_type'] . "] " . ($body ? "- {$body}" : "") . "\n";
|
|
}
|
|
}
|
|
$outputText .= "\n\n";
|
|
}
|
|
}
|
|
|
|
// 4. Write the file to public directory
|
|
$publicDir = __DIR__ . '/../../public';
|
|
$filePath = $publicDir . '/whatsapp_chats_history.txt';
|
|
file_put_contents($filePath, $outputText);
|
|
|
|
$response->json([
|
|
'status' => 'success',
|
|
'message' => 'Chat history exported successfully from database',
|
|
'download_url' => '/whatsapp_chats_history.txt'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
error_log("[SuperAdminController Export Error] " . $e->getMessage());
|
|
$response->status(500)->json([
|
|
'error' => 'Failed to export chats from database: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
}
|