init backend presensi
This commit is contained in:
1
app/Modules/Devices/Controllers/.gitkeep
Normal file
1
app/Modules/Devices/Controllers/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# Devices Module - Controllers
|
||||
72
app/Modules/Devices/Controllers/DeviceAuthController.php
Normal file
72
app/Modules/Devices/Controllers/DeviceAuthController.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Devices\Controllers;
|
||||
|
||||
use App\Core\BaseApiController;
|
||||
use App\Modules\Devices\Services\DeviceAuthService;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Device Authentication Controller
|
||||
*
|
||||
* Handles device authentication endpoints.
|
||||
*/
|
||||
class DeviceAuthController extends BaseApiController
|
||||
{
|
||||
protected DeviceAuthService $deviceAuthService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->deviceAuthService = new DeviceAuthService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Device login endpoint
|
||||
*
|
||||
* Authenticates device using device_code and api_key.
|
||||
*
|
||||
* POST /api/device/login
|
||||
* Body: { "device_code": "", "api_key": "" }
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function login(): ResponseInterface
|
||||
{
|
||||
// Get JSON input
|
||||
$input = $this->request->getJSON(true);
|
||||
|
||||
// Validate input
|
||||
if (empty($input['device_code']) || empty($input['api_key'])) {
|
||||
return $this->errorResponse(
|
||||
'device_code and api_key are required',
|
||||
null,
|
||||
null,
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
// Authenticate device
|
||||
$deviceData = $this->deviceAuthService->authenticate(
|
||||
$input['device_code'],
|
||||
$input['api_key']
|
||||
);
|
||||
|
||||
if (!$deviceData) {
|
||||
return $this->errorResponse(
|
||||
'Invalid device credentials',
|
||||
null,
|
||||
null,
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
// Return success response
|
||||
return $this->successResponse(
|
||||
[
|
||||
'device_id' => $deviceData['device_id'],
|
||||
'device_code' => $deviceData['device_code'],
|
||||
],
|
||||
'Device authenticated'
|
||||
);
|
||||
}
|
||||
}
|
||||
80
app/Modules/Devices/Controllers/DeviceController.php
Normal file
80
app/Modules/Devices/Controllers/DeviceController.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Devices\Controllers;
|
||||
|
||||
use App\Core\BaseApiController;
|
||||
use App\Modules\Devices\Models\DeviceModel;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Device Controller
|
||||
*
|
||||
* Admin-only management for device configuration (geo-fence, etc).
|
||||
*/
|
||||
class DeviceController extends BaseApiController
|
||||
{
|
||||
/**
|
||||
* PUT /api/devices/{id}
|
||||
*
|
||||
* Body (JSON):
|
||||
* - latitude: float|null
|
||||
* - longitude: float|null
|
||||
* - radius_meters: int|null
|
||||
*/
|
||||
public function update($id): ResponseInterface
|
||||
{
|
||||
$id = (int) $id;
|
||||
if ($id <= 0) {
|
||||
return $this->errorResponse('Invalid device id', null, null, ResponseInterface::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$payload = $this->request->getJSON(true) ?? [];
|
||||
|
||||
$lat = $payload['latitude'] ?? null;
|
||||
$lng = $payload['longitude'] ?? null;
|
||||
$radius = $payload['radius_meters'] ?? null;
|
||||
|
||||
// Normalize empty strings to null
|
||||
$lat = ($lat === '' || $lat === null) ? null : $lat;
|
||||
$lng = ($lng === '' || $lng === null) ? null : $lng;
|
||||
$radius = ($radius === '' || $radius === null) ? null : $radius;
|
||||
|
||||
// Basic validation
|
||||
if ($lat !== null && !is_numeric($lat)) {
|
||||
return $this->errorResponse('Latitude harus berupa angka atau kosong', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
if ($lng !== null && !is_numeric($lng)) {
|
||||
return $this->errorResponse('Longitude harus berupa angka atau kosong', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
if ($radius !== null && (!is_numeric($radius) || (int) $radius < 0)) {
|
||||
return $this->errorResponse('Radius harus berupa angka >= 0 atau kosong', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
// If one of lat/lng/radius set, require all three
|
||||
$hasAny = $lat !== null || $lng !== null || $radius !== null;
|
||||
if ($hasAny) {
|
||||
if ($lat === null || $lng === null || $radius === null) {
|
||||
return $this->errorResponse('Jika mengatur zona, latitude, longitude, dan radius wajib diisi semua', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'latitude' => $lat !== null ? (float) $lat : null,
|
||||
'longitude' => $lng !== null ? (float) $lng : null,
|
||||
'radius_meters' => $radius !== null ? (int) $radius : null,
|
||||
];
|
||||
|
||||
$model = new DeviceModel();
|
||||
$device = $model->find($id);
|
||||
if (!$device) {
|
||||
return $this->errorResponse('Device tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$model->update($id, $data)) {
|
||||
return $this->errorResponse('Gagal menyimpan konfigurasi device', $model->errors(), null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
return $this->successResponse(null, 'Konfigurasi geo-fence device berhasil disimpan');
|
||||
}
|
||||
}
|
||||
|
||||
71
app/Modules/Devices/Controllers/MobileController.php
Normal file
71
app/Modules/Devices/Controllers/MobileController.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Devices\Controllers;
|
||||
|
||||
use App\Core\BaseApiController;
|
||||
use App\Modules\Geo\Models\ZoneModel;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Mobile Support Controller
|
||||
*
|
||||
* Endpoints for Android app (ping, bootstrap). No authentication required.
|
||||
*/
|
||||
class MobileController extends BaseApiController
|
||||
{
|
||||
/**
|
||||
* Default check-in late tolerance in minutes (must match AttendanceCheckinService)
|
||||
*/
|
||||
protected int $checkinToleranceMinutes = 10;
|
||||
|
||||
/**
|
||||
* GET /api/mobile/ping
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function ping(): ResponseInterface
|
||||
{
|
||||
$data = [
|
||||
'server_time' => date('Y-m-d H:i:s'),
|
||||
'api_version' => '1.0',
|
||||
];
|
||||
return $this->successResponse($data, 'Mobile connected');
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/mobile/bootstrap
|
||||
*
|
||||
* Returns active zones, device validation rules, and checkin tolerance for app startup.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function bootstrap(): ResponseInterface
|
||||
{
|
||||
$zoneModel = new ZoneModel();
|
||||
$activeZones = $zoneModel->findAllActive();
|
||||
|
||||
$zonesData = [];
|
||||
foreach ($activeZones as $zone) {
|
||||
$zonesData[] = [
|
||||
'zone_code' => $zone->zone_code,
|
||||
'zone_name' => $zone->zone_name,
|
||||
'latitude' => (float) $zone->latitude,
|
||||
'longitude' => (float) $zone->longitude,
|
||||
'radius_meters' => (int) $zone->radius_meters,
|
||||
];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'server_timestamp' => gmdate('Y-m-d\TH:i:s\Z'), // ISO 8601 UTC — for device_time_offset = server_time - device_time
|
||||
'server_timezone' => 'Asia/Jakarta', // WIB — for display / jadwal sekolah
|
||||
'active_zones' => $zonesData,
|
||||
'device_validation_rules' => [
|
||||
'require_device_code' => true,
|
||||
'require_api_key' => true,
|
||||
],
|
||||
'checkin_tolerance_minutes' => $this->checkinToleranceMinutes,
|
||||
];
|
||||
|
||||
return $this->successResponse($data, 'Bootstrap data');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user