UpgradeController.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <?php
  2. namespace App\Controllers;
  3. use Psr\Http\Message\ResponseInterface as Response;
  4. use Psr\Http\Message\ServerRequestInterface as Request;
  5. use RuntimeException;
  6. use ZipArchive;
  7. class UpgradeController extends Controller
  8. {
  9. const GITHUB_SOURCE_API = 'https://api.github.com/repos/SergiX44/XBackBone/releases';
  10. /**
  11. * @param Response $response
  12. *
  13. * @return Response
  14. */
  15. public function upgrade(Response $response): Response
  16. {
  17. if (!is_writable(BASE_DIR)) {
  18. $this->session->alert(lang('path_not_writable', BASE_DIR), 'warning');
  19. return redirect($response, route('system'));
  20. }
  21. try {
  22. $json = $this->getApiJson();
  23. } catch (RuntimeException $e) {
  24. $this->session->alert($e->getMessage(), 'danger');
  25. return redirect($response, route('system'));
  26. }
  27. if (version_compare($json[0]->tag_name, PLATFORM_VERSION, '<=')) {
  28. $this->session->alert(lang('already_latest_version'), 'warning');
  29. return redirect($response, route('system'));
  30. }
  31. $tmpFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.'xbackbone_update.zip';
  32. if (file_put_contents($tmpFile, file_get_contents($json[0]->assets[0]->browser_download_url)) === false) {
  33. $this->session->alert(lang('cannot_retrieve_file'), 'danger');
  34. return redirect($response, route('system'));
  35. }
  36. if (filesize($tmpFile) !== $json[0]->assets[0]->size) {
  37. $this->session->alert(lang('file_size_no_match'), 'danger');
  38. return redirect($response, route('system'));
  39. }
  40. $config = require BASE_DIR.'config.php';
  41. $config['maintenance'] = true;
  42. file_put_contents(BASE_DIR.'config.php', '<?php'.PHP_EOL.'return '.var_export($config, true).';');
  43. $currentFiles = array_merge(
  44. glob_recursive(BASE_DIR.'app/*'),
  45. glob_recursive(BASE_DIR.'bin/*'),
  46. glob_recursive(BASE_DIR.'bootstrap/*'),
  47. glob_recursive(BASE_DIR.'resources/templates/*'),
  48. glob_recursive(BASE_DIR.'resources/lang/*'),
  49. glob_recursive(BASE_DIR.'resources/schemas/*'),
  50. glob_recursive(BASE_DIR.'static/*')
  51. );
  52. removeDirectory(BASE_DIR.'vendor/');
  53. $updateZip = new ZipArchive();
  54. $updateZip->open($tmpFile);
  55. for ($i = 0; $i < $updateZip->numFiles; $i++) {
  56. $nameIndex = $updateZip->getNameIndex($i);
  57. $updateZip->extractTo(BASE_DIR, $nameIndex);
  58. if (($key = array_search(rtrim(BASE_DIR.$nameIndex, '/'), $currentFiles)) !== false) {
  59. unset($currentFiles[$key]);
  60. }
  61. }
  62. foreach ($currentFiles as $extraneous) {
  63. unlink($extraneous);
  64. }
  65. $updateZip->close();
  66. unlink($tmpFile);
  67. return redirect($response, urlFor('/install'));
  68. }
  69. /**
  70. * @param Request $request
  71. * @param Response $response
  72. *
  73. * @return Response
  74. */
  75. public function checkForUpdates(Request $request, Response $response): Response
  76. {
  77. $jsonResponse = [
  78. 'status' => null,
  79. 'message' => null,
  80. 'upgrade' => false,
  81. ];
  82. $acceptPrerelease = param($request, 'prerelease', 'false') === 'true';
  83. try {
  84. $json = $this->getApiJson();
  85. $jsonResponse['status'] = 'OK';
  86. foreach ($json as $release) {
  87. if (version_compare($release->tag_name, PLATFORM_VERSION, '>') && ($release->prerelease === $acceptPrerelease)) {
  88. $jsonResponse['message'] = lang('new_version_available', $release->tag_name);
  89. $jsonResponse['upgrade'] = true;
  90. break;
  91. }
  92. if (version_compare($release->tag_name, PLATFORM_VERSION, '<=')) {
  93. $jsonResponse['message'] = lang('already_latest_version');
  94. $jsonResponse['upgrade'] = false;
  95. break;
  96. }
  97. }
  98. } catch (RuntimeException $e) {
  99. $jsonResponse['status'] = 'ERROR';
  100. $jsonResponse['message'] = $e->getMessage();
  101. }
  102. return json($response, $jsonResponse);
  103. }
  104. protected function getApiJson()
  105. {
  106. $opts = [
  107. 'http' => [
  108. 'method' => 'GET',
  109. 'header' => [
  110. 'User-Agent: XBackBone-App',
  111. 'Accept: application/vnd.github.v3+json',
  112. ],
  113. ],
  114. ];
  115. $data = @file_get_contents(self::GITHUB_SOURCE_API, false, stream_context_create($opts));
  116. if ($data === false) {
  117. throw new RuntimeException('Cannot contact the Github API. Try again.');
  118. }
  119. return json_decode($data);
  120. }
  121. }