Files
fitme3070.pt/api/delete_hub-content.php
T
andreandClaude Opus 4.8 3d460e50d1 Retire hub-content API endpoint via delete_ rename
Rename api/hub-content.php -> api/delete_hub-content.php so the old
content-pull path returns 404, disabling the hub social-post endpoint
without removing the implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:19:24 +01:00

146 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Content endpoint for the id.andmore.pt / hub social-post generator (hub pull).
//
// Server-to-server, no session: the hub signs each request with the shared
// HUB_CONTENT_SECRET (.env; generated on id.andmore.pt → Plataformas →
// fitme3070 → "Gerar segredo de conteúdo"):
// X-Hub-Date: ISO-8601 timestamp (±10 min tolerance)
// X-Hub-Token: hash_hmac('sha256', date . path, secret)
//
// Response is the generic hub envelope. One item type:
// event — the weekly class schedule (dias da semana + as aulas nesses dias).
// Each recurring class is emitted as an event whose date_start is the
// next occurrence of that weekday/time, so the hub renders the day
// name automatically; the weekday is also given explicitly in fields.
//
// GET params: from=Y-m-d to=Y-m-d type=event (all optional; the schedule is
// recurring, so the date range is not used to filter).
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../config/functions.php';
header('Content-Type: application/json; charset=utf-8');
$secret = (string) env('HUB_CONTENT_SECRET', '');
if ($secret === '') {
http_response_code(503);
echo json_encode(['error' => 'HUB_CONTENT_SECRET não configurado nesta plataforma.']);
exit;
}
$date = (string) ($_SERVER['HTTP_X_HUB_DATE'] ?? '');
$token = (string) ($_SERVER['HTTP_X_HUB_TOKEN'] ?? '');
$path = (string) (parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? '');
$ts = $date !== '' ? strtotime($date) : false;
if ($token === '' || $ts === false || abs(time() - $ts) > 600
|| !hash_equals(hash_hmac('sha256', $date . $path, $secret), $token)) {
http_response_code(403);
echo json_encode(['error' => 'Assinatura inválida.']);
exit;
}
session_write_close(); // not needed past auth; release the session lock
$type = (string) ($_GET['type'] ?? '');
// Optional category filter: ?category=segunda or ?categories=segunda,quarta.
// For this platform the category dimension is the weekday. Empty = all.
$cat_filter = array_filter(array_map(
fn($c) => strtolower(trim($c)),
preg_split('/[,]/', (string) ($_GET['categories'] ?? $_GET['category'] ?? ''))
));
// Weekday enum → display label and ISO day-of-week (1=Mon … 7=Sun).
$WEEKDAYS = [
'segunda' => ['Segunda-feira', 1],
'terça' => ['Terça-feira', 2],
'quarta' => ['Quarta-feira', 3],
'quinta' => ['Quinta-feira', 4],
'sexta' => ['Sexta-feira', 5],
'sábado' => ['Sábado', 6],
'domingo' => ['Domingo', 7],
];
$LOCALS = ['sala1' => 'Sala 1', 'sala2' => 'Sala 2', 'crossfit' => 'CrossFit'];
// Next occurrence (today or in the coming week) of a given weekday at $time.
$next_occurrence = static function (int $isoDow, string $time): DateTime {
$dt = new DateTime('today');
$diff = ($isoDow - (int) $dt->format('N') + 7) % 7; // 0 = today
if ($diff > 0) $dt->modify("+{$diff} day");
[$h, $m, $s] = array_pad(explode(':', $time), 3, '0');
$dt->setTime((int) $h, (int) $m, (int) $s);
return $dt;
};
$items = [];
// ── event: weekly class schedule ────────────────────────────────────────────
if ($type === '' || $type === 'event') {
// db_horario_list() already joins modalidades and orders segunda→domingo,
// then by start time; restrict to live modalidades (not soft-deleted).
$rows = $pdo->query("
SELECT h.id, h.dia_semana, h.local,
m.nome AS modalidade_nome,
TIME_FORMAT(h.hora_inicio, '%H:%i') AS hora_inicio_fmt,
TIME_FORMAT(h.hora_fim, '%H:%i') AS hora_fim_fmt
FROM horario h
JOIN modalidades m ON m.m_id = h.id_modalidade AND m.deleted_at IS NULL
ORDER BY FIELD(h.dia_semana,'segunda','terça','quarta','quinta','sexta','sábado','domingo'),
h.hora_inicio ASC
")->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) {
$dia = (string) $r['dia_semana'];
if (!isset($WEEKDAYS[$dia])) continue; // unknown / NULL weekday — skip
if ($cat_filter && !in_array($dia, $cat_filter, true)) continue; // category filter
[$diaLabel, $isoDow] = $WEEKDAYS[$dia];
$ini = (string) $r['hora_inicio_fmt'];
$fim = (string) $r['hora_fim_fmt'];
$hora = $fim !== '' ? ($ini . '–' . $fim) : $ini;
$local = $LOCALS[(string) $r['local']] ?? '';
$start = $next_occurrence($isoDow, $ini !== '' ? $ini . ':00' : '00:00:00');
$end = $fim !== '' ? $next_occurrence($isoDow, $fim . ':00') : null;
$fields = ['Dia' => $diaLabel, 'Hora' => $hora];
if ($local !== '') $fields['Local'] = $local;
$items[] = [
'id' => 'aula-' . (int) $r['id'],
'type' => 'event',
'title' => (string) $r['modalidade_nome'],
'subtitle' => $hora . ($local !== '' ? ' · ' . $local : ''),
'date_start' => $start->format('c'),
'date_end' => $end ? $end->format('c') : '',
'url' => base_url('home/horario'),
'category' => $dia, // machine key (weekday enum value)
'category_label' => $diaLabel, // human label
'fields' => $fields,
'media' => [],
];
}
}
// Distinct categories present, in item order (segunda→domingo), with counts —
// lets the hub offer a category filter without re-deriving it.
$categories = [];
foreach ($items as $it) {
$k = $it['category'];
if ($k === '') continue;
if (!isset($categories[$k])) {
$categories[$k] = ['key' => $k, 'label' => $it['category_label'], 'count' => 0];
}
$categories[$k]['count']++;
}
echo json_encode([
'app' => 'fitme3070',
'generated_at' => gmdate('c'),
'item_types' => array_values(array_unique(array_column($items, 'type'))),
'category_label'=> 'Dia da semana', // what the category dimension means here
'categories' => array_values($categories),
'items' => $items,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);