194 lines
4.0 KiB
PHP
194 lines
4.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
$token = getenv( 'THERMOPRO_BRIDGE_TOKEN' ) ?: 'dev-secret';
|
|
$dataPath = __DIR__ . '/data';
|
|
$latestPath = $dataPath . '/latest.json';
|
|
$logPath = $dataPath . '/readings.ndjson';
|
|
|
|
if ( ! is_dir( $dataPath ) ) {
|
|
mkdir( $dataPath, 0775, true );
|
|
}
|
|
|
|
function sendJson( array $payload, int $statusCode = 200 ): void {
|
|
http_response_code( $statusCode );
|
|
header( 'Content-Type: application/json' );
|
|
|
|
echo json_encode(
|
|
$payload,
|
|
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
|
|
);
|
|
|
|
exit;
|
|
}
|
|
|
|
function getRequestPath(): string {
|
|
return parse_url( $_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH ) ?: '/';
|
|
}
|
|
|
|
function readJsonBody(): array {
|
|
$body = file_get_contents( 'php://input' );
|
|
$payload = json_decode( $body ?: '', true );
|
|
|
|
if ( ! is_array( $payload ) ) {
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'Invalid JSON body',
|
|
),
|
|
400
|
|
);
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
function requireBridgeToken( string $expectedToken ): void {
|
|
$providedToken = $_SERVER['HTTP_X_BRIDGE_TOKEN'] ?? '';
|
|
|
|
if ( ! hash_equals( $expectedToken, $providedToken ) ) {
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'Invalid bridge token',
|
|
),
|
|
401
|
|
);
|
|
}
|
|
}
|
|
|
|
function validateReading( array $payload ): void {
|
|
$requiredFields = array(
|
|
'deviceId',
|
|
'connected',
|
|
'battery',
|
|
'unit',
|
|
'probes',
|
|
'readingTime',
|
|
);
|
|
|
|
foreach ( $requiredFields as $field ) {
|
|
if ( ! array_key_exists( $field, $payload ) ) {
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'Missing required field: ' . $field,
|
|
),
|
|
422
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( ! is_array( $payload['probes'] ) ) {
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'The probes field must be an array',
|
|
),
|
|
422
|
|
);
|
|
}
|
|
}
|
|
|
|
$path = getRequestPath();
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
|
|
if ( $method === 'POST' && $path === '/api/thermopro/readings' ) {
|
|
requireBridgeToken( $token );
|
|
|
|
$payload = readJsonBody();
|
|
validateReading( $payload );
|
|
|
|
$payload['serverReceivedAt'] = gmdate( DATE_ATOM );
|
|
|
|
file_put_contents(
|
|
$latestPath,
|
|
json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES )
|
|
);
|
|
|
|
file_put_contents(
|
|
$logPath,
|
|
json_encode( $payload, JSON_UNESCAPED_SLASHES ) . PHP_EOL,
|
|
FILE_APPEND | LOCK_EX
|
|
);
|
|
|
|
sendJson(
|
|
array(
|
|
'ok' => true,
|
|
'receivedAt' => $payload['serverReceivedAt'],
|
|
)
|
|
);
|
|
}
|
|
|
|
if ( $method === 'GET' && $path === '/api/thermopro/latest' ) {
|
|
if ( ! file_exists( $latestPath ) ) {
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'No readings received yet',
|
|
),
|
|
404
|
|
);
|
|
}
|
|
|
|
header( 'Content-Type: application/json' );
|
|
readfile( $latestPath );
|
|
exit;
|
|
}
|
|
|
|
if ( $method === 'GET' && ( $path === '/' || $path === '/status' ) ) {
|
|
$latest = null;
|
|
|
|
if ( file_exists( $latestPath ) ) {
|
|
$latest = json_decode( file_get_contents( $latestPath ), true );
|
|
}
|
|
|
|
header( 'Content-Type: text/html; charset=utf-8' );
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>ThermoPro Bridge POC</title>
|
|
<meta http-equiv="refresh" content="10">
|
|
<style>
|
|
body {
|
|
font-family: system-ui, sans-serif;
|
|
margin: 2rem;
|
|
max-width: 900px;
|
|
}
|
|
|
|
pre {
|
|
background: #f4f4f4;
|
|
overflow: auto;
|
|
padding: 1rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>ThermoPro Bridge POC</h1>
|
|
|
|
<?php if ( $latest === null ) { ?>
|
|
<p>No readings received yet.</p>
|
|
<?php } else { ?>
|
|
<p>
|
|
Latest reading received at:
|
|
<strong><?php echo htmlspecialchars( $latest['serverReceivedAt'] ?? 'unknown', ENT_QUOTES, 'UTF-8' ); ?></strong>
|
|
</p>
|
|
|
|
<pre><?php echo htmlspecialchars( json_encode( $latest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ), ENT_QUOTES, 'UTF-8' ); ?></pre>
|
|
<?php } ?>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
exit;
|
|
}
|
|
|
|
sendJson(
|
|
array(
|
|
'ok' => false,
|
|
'error' => 'Not found',
|
|
),
|
|
404
|
|
);
|