init backend presensi
This commit is contained in:
105
app/Modules/Dashboard/Controllers/RealtimeStreamController.php
Normal file
105
app/Modules/Dashboard/Controllers/RealtimeStreamController.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Dashboard\Controllers;
|
||||
|
||||
use App\Core\BaseApiController;
|
||||
use App\Modules\Auth\Services\AuthService;
|
||||
use App\Modules\Dashboard\Services\DashboardRealtimeService;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Realtime Stream Controller
|
||||
*
|
||||
* Server-Sent Events (SSE) for live attendance updates.
|
||||
* Role-aware: stream content is filtered by current user's roles (ADMIN / WALI_KELAS / GURU_MAPEL / ORANG_TUA).
|
||||
*/
|
||||
class RealtimeStreamController extends BaseApiController
|
||||
{
|
||||
protected DashboardRealtimeService $realtimeService;
|
||||
protected AuthService $authService;
|
||||
|
||||
/** Stream interval in seconds */
|
||||
protected int $intervalSeconds = 2;
|
||||
|
||||
/** Max stream duration in seconds */
|
||||
protected int $timeoutSeconds = 60;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->realtimeService = new DashboardRealtimeService();
|
||||
$this->authService = new AuthService();
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/dashboard/stream
|
||||
*
|
||||
* SSE stream: every 2s check for new attendance, send event; timeout after 60s.
|
||||
* Uses after_id for incremental feed. User context from session applied for role filtering.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function index(): ResponseInterface
|
||||
{
|
||||
ignore_user_abort(true);
|
||||
set_time_limit(0);
|
||||
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
$this->response->setHeader('Content-Type', 'text/event-stream');
|
||||
$this->response->setHeader('Cache-Control', 'no-cache');
|
||||
$this->response->setHeader('Connection', 'keep-alive');
|
||||
$this->response->setHeader('X-Accel-Buffering', 'no'); // nginx
|
||||
$this->response->setBody('');
|
||||
|
||||
if (function_exists('apache_setenv')) {
|
||||
@apache_setenv('no-gzip', '1');
|
||||
}
|
||||
@ini_set('output_buffering', 'off');
|
||||
@ini_set('zlib.output_compression', false);
|
||||
|
||||
$this->response->sendHeaders();
|
||||
|
||||
$currentUser = $this->authService->currentUser();
|
||||
$startTime = time();
|
||||
$lastId = (int) ($this->request->getGet('after_id') ?? 0);
|
||||
|
||||
while (true) {
|
||||
if (time() - $startTime >= $this->timeoutSeconds) {
|
||||
$this->sendEvent('timeout', ['message' => 'Stream ended after ' . $this->timeoutSeconds . 's']);
|
||||
break;
|
||||
}
|
||||
|
||||
$rows = $this->realtimeService->getAttendanceSinceId($lastId, 50, $currentUser);
|
||||
|
||||
if (empty($rows)) {
|
||||
$this->sendEvent('heartbeat', ['ts' => gmdate('Y-m-d\TH:i:s\Z')]);
|
||||
} else {
|
||||
foreach ($rows as $row) {
|
||||
$this->sendEvent('attendance', $row);
|
||||
$lastId = max($lastId, $row['id']);
|
||||
}
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
sleep($this->intervalSeconds);
|
||||
}
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send one SSE event (event name + data line)
|
||||
*
|
||||
* @param string $event Event name
|
||||
* @param array $data Data to send as JSON
|
||||
*/
|
||||
protected function sendEvent(string $event, array $data): void
|
||||
{
|
||||
echo 'event: ' . $event . "\n";
|
||||
echo 'data: ' . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
|
||||
flush();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user