changelog_api.php
2.53 KB
<?php
require_once __DIR__ . '/../bootstrap.php';
requireAuth();
header('Content-Type: application/json; charset=utf-8');
$version = trim($_GET['v'] ?? '');
if ($version === '') {
echo json_encode(['ok' => false, 'error' => 'Missing version']);
exit;
}
// Strict version format to avoid path traversal.
if (!preg_match('/^[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9._-]+)?$/', $version)) {
echo json_encode(['ok' => false, 'error' => 'Invalid version']);
exit;
}
$logDir = realpath(__DIR__ . '/../assets/changelog');
if (!$logDir || !is_dir($logDir)) {
echo json_encode(['ok' => false, 'error' => 'Changelog folder not found']);
exit;
}
$filename = 'update_log_' . $version . '.md';
// Preferred: read from single archive
$archive = $logDir . '/updates.zip';
if (is_readable($archive)) {
$zip = class_exists('ZipArchive') ? new ZipArchive() : null;
if ($zip && $zip->open($archive) === true) {
$content = $zip->getFromName($filename);
$stat = $zip->statName($filename);
$zip->close();
if ($content !== false) {
$content = str_replace("
", "
", (string)$content);
if (strlen($content) > 200000) {
$content = substr($content, 0, 200000) . "
…";
}
echo json_encode([
'ok' => true,
'version' => $version,
'content' => $content,
'mtime' => isset($stat['mtime']) ? (int)$stat['mtime'] : null,
'source' => 'archive',
'legacy' => (strpos($content, 'Legacy update log') !== false && strpos($content, 'Not recorded') !== false),
]);
exit;
}
}
}
// Fallback: legacy loose files
$path = realpath($logDir . DIRECTORY_SEPARATOR . $filename);
// Ensure resolved path stays inside changelog directory.
if (!$path || strncmp($path, $logDir, strlen($logDir)) !== 0 || !is_readable($path)) {
echo json_encode(['ok' => false, 'error' => 'Update log not found']);
exit;
}
$content = file_get_contents($path);
if ($content === false) {
echo json_encode(['ok' => false, 'error' => 'Unable to read update log']);
exit;
}
$content = str_replace("
", "
", $content);
if (strlen($content) > 200000) {
$content = substr($content, 0, 200000) . "
…";
}
echo json_encode([
'ok' => true,
'version' => $version,
'content' => $content,
'mtime' => @filemtime($path) ?: null,
'source' => 'files',
'legacy' => (strpos($content, 'Legacy update log') !== false && strpos($content, 'Not recorded') !== false),
]);