Files
sunscope/api/journal-save.php
T
fraxle 71860b9dd9 5.4.0
Restructure
New Journal entries
Colour tabs update with wind
2026-08-31 15:12:45 +01:00

103 lines
4.1 KiB
PHP

<?php
// POST { date, endDate?, note, id?, profile?, solarEnv?, reading?, locationName?, lat?, lon? }
// -> creates a new entry, or updates one the caller owns when id is given.
// { ok:true, id }.
//
// endDate is optional - a note can span a period (e.g. a holiday week)
// rather than a single day. Omitted/equal-to-date means a single-day entry.
//
// profile/solarEnv/reading/locationName/lat/lon are an optional attached
// "reading" - a computed felt-temperature number the browser derives
// client-side from the live 14-day forecast (same compute.js the main app
// uses, same peak-of-day rule the day tabs use, taken across the whole
// range for a multi-day entry). The server never touches the physics; it
// just stores whatever number the browser already computed, the same way
// it stores the note text. profile AND solarEnv are both stored because
// both change the number - the dashboard filters its entry list and graph
// down to whichever combination is currently selected, so a Desert reading
// never turns up mixed into a Beach trend.
require __DIR__ . '/../lib/bootstrap.php';
require_method('POST');
$user = require_user();
$body = json_body();
$date = (string)($body['date'] ?? '');
$note = trim((string)($body['note'] ?? ''));
$id = isset($body['id']) ? (int)$body['id'] : null;
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
json_error('Invalid date.');
}
$endDate = null;
if (isset($body['endDate']) && $body['endDate'] !== null && $body['endDate'] !== '') {
$endDate = (string)$body['endDate'];
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
json_error('Invalid end date.');
}
if ($endDate < $date) json_error('End date must be on or after the start date.');
if ($endDate === $date) $endDate = null; // same day - store as a single-day entry
}
if ($note === '') {
json_error('Note cannot be empty.');
}
if (strlen($note) > 4000) {
json_error('Note is too long.');
}
$profile = null;
$solarEnv = null;
$reading = null;
$locationName = null;
$lat = null;
$lon = null;
if (isset($body['reading']) && $body['reading'] !== null && $body['reading'] !== '') {
if (!is_numeric($body['reading'])) json_error('Invalid reading.');
$reading = round((float)$body['reading'], 1);
if ($reading < -100 || $reading > 150) json_error('Reading out of range.');
$profile = (string)($body['profile'] ?? '');
if (!in_array($profile, ['basic', 'home', 'vehicle', 'pets'], true)) {
json_error('Invalid profile.');
}
$solarEnv = (string)($body['solarEnv'] ?? '');
if (!in_array($solarEnv, ['open', 'urban', 'beach', 'river', 'forest', 'openwater', 'alpine', 'desert'], true)) {
json_error('Invalid solar model.');
}
$locationName = trim((string)($body['locationName'] ?? ''));
if ($locationName === '' || strlen($locationName) > 255) json_error('Invalid location.');
if (!is_numeric($body['lat'] ?? null) || !is_numeric($body['lon'] ?? null)) {
json_error('Invalid coordinates.');
}
$lat = round((float)$body['lat'], 5);
$lon = round((float)$body['lon'], 5);
if ($lat < -90 || $lat > 90 || $lon < -180 || $lon > 180) json_error('Invalid coordinates.');
}
if ($id) {
// Ownership enforced in the WHERE clause, not a prior SELECT.
$stmt = db()->prepare(
'UPDATE journal_entries
SET entry_date = ?, end_date = ?, note = ?, profile = ?, solar_env = ?, reading = ?, location_name = ?, lat = ?, lon = ?
WHERE id = ? AND user_id = ?'
);
$stmt->execute([$date, $endDate, $note, $profile, $solarEnv, $reading, $locationName, $lat, $lon, $id, $user['id']]);
if ($stmt->rowCount() === 0) {
json_error('Entry not found.', 404);
}
json_out(['ok' => true, 'id' => $id]);
}
$stmt = db()->prepare(
'INSERT INTO journal_entries (user_id, entry_date, end_date, note, profile, solar_env, reading, location_name, lat, lon)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
);
$stmt->execute([$user['id'], $date, $endDate, $note, $profile, $solarEnv, $reading, $locationName, $lat, $lon]);
json_out(['ok' => true, 'id' => (int)db()->lastInsertId()]);