Files
2026-06-06 10:23:49 -05:00

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
);