pgupgrade.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. function doAPIRequestWithCallback (meth, mod, func, handler, errorHandler, args) {
  2. 'use strict';
  3. let oReq = new XMLHttpRequest();
  4. oReq.onreadystatechange = function() {
  5. if (this.readyState === XMLHttpRequest.DONE) {
  6. if( this.status === 200 ) {
  7. handler(this.responseText);
  8. } else {
  9. errorHandler(meth, mod, func, this.status, this.responseText);
  10. }
  11. }
  12. }
  13. let argarr = [ `module=${mod}`, `function=${func}` ];
  14. if( typeof args === 'object' ) {
  15. Object.keys(args).forEach( function(argument) {
  16. argarr.push(`${argument}=${args[argument]}`);
  17. });
  18. }
  19. let argstr = argarr.join("&");
  20. if( meth === 'GET' ) {
  21. oReq.open( meth, `api.cgi?${argstr}`, true );
  22. oReq.send();
  23. } else if ( meth === 'POST' ) {
  24. oReq.open( meth, "api.cgi", true );
  25. oReq.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
  26. oReq.send(argstr);
  27. }
  28. return false;
  29. }
  30. function generalErrorHandler(meth, mod, func, code, txt) {
  31. console.log(txt);
  32. alert(`${meth} call to Troglodyne::API::${mod}::${func} failed with error code ${code}! Please see the JS console for details.`);
  33. return false;
  34. }
  35. function safeParseJSON(txt) {
  36. let obj = {};
  37. try {
  38. obj = JSON.parse(txt);
  39. } catch(e) {
  40. console.log(txt);
  41. return { "error": e };
  42. }
  43. return obj;
  44. }
  45. function versionHandler (resp) {
  46. 'use strict';
  47. let obj = safeParseJSON(resp);
  48. if(obj.result === 1) {
  49. // Construct version warning/display
  50. let pgVersion = obj.data.installed_version.major + '.' + obj.data.installed_version.minor;
  51. let elem = document.getElementById('psqlVersion');
  52. let html = `<strong>${pgVersion}</strong>`;
  53. if( parseFloat(pgVersion) < parseFloat(obj.data.minimum_supported_version) ) {
  54. elem.classList.add('callout', 'callout-danger');
  55. html += ' -- You are using a version of PostgreSQL Server that is no longer supported by ';
  56. html += '<a href="https://www.postgresql.org/support/versioning/" title="PostgreSQL Supported versions page">postgresql.org</a>!<br>';
  57. if( obj.data.eol_versions.hasOwnProperty(pgVersion) ) {
  58. html += "<strong>EOL</strong> -- " + new Date(obj.data.eol_versions[pgVersion].EOL * 1000).toLocaleString( undefined, { year: 'numeric', month: 'long', day: 'numeric' } ) + "<br>";
  59. }
  60. html += "<strong>Immediate upgrade is recommended.</strong>";
  61. }
  62. elem.innerHTML = html;
  63. // Now let's build the table
  64. let rows = '';
  65. for ( var version of Object.keys(obj.data.available_versions).sort(function(a,b) { return parseFloat(b) - parseFloat(a) }) ) {
  66. if( parseFloat(pgVersion) >= parseFloat(version) ) {
  67. continue;
  68. }
  69. rows +=
  70. `<tr id="pgVersionRow-${version}">
  71. <td>
  72. <input type="radio" name="selectedVersion" value="${version}" onclick="document.getElementById('submit').disabled = false;"></input>
  73. ${version}
  74. </td>
  75. <td><ul>`;
  76. obj.data.available_versions[version].features.forEach(function(feature) {
  77. rows += `<li>${feature}</li>`;
  78. });
  79. rows += `</ul></td>
  80. <td>
  81. ${new Date(obj.data.available_versions[version].release * 1000).toLocaleString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}
  82. </td>
  83. <td>
  84. ${new Date(obj.data.available_versions[version].EOL * 1000).toLocaleString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}
  85. </td>
  86. </tr>`;
  87. }
  88. if( !rows.length ) {
  89. rows = '<tr id="noAvailableVersions"><td colspan=4>No newer versions are currently available for install. ';
  90. rows += '<a href="https://troglodyne.net/better-postgres-for-cpanel#FAQ-YUNO-PG11" title="Versions FAQ">Click here for more information.</a>';
  91. rows += '</td></tr>';
  92. }
  93. document.getElementById('loadingCell').remove();
  94. document.querySelector('#upgradeForm > table > tbody').innerHTML = rows;
  95. } else {
  96. console.log(obj.error);
  97. }
  98. }
  99. function doInstallScroller (resp) {
  100. 'use strict';
  101. let obj = safeParseJSON(resp);
  102. let upgradeWell = document.getElementById('upgradeWell');
  103. let submitBtn = document.getElementById('submit');
  104. if(obj.result === 1) {
  105. if(obj.data.exit_code !== 0) {
  106. upgradeWell.textContent += obj.data.last_yum_command + " reported nonzero exit code (" + obj.data.exit_code + "):\n[STDOUT] " + obj.data.stdout + "\n[STDERR] " + obj.data.stderr;
  107. submitBtn.textContent = 'Re-Try';
  108. submitBtn.disabled = false;
  109. return false;
  110. }
  111. if(obj.data.already_installed) {
  112. upgradeWell.textContent += obj.data.last_yum_command + " reports the community repository is already installed: " + obj.data.stdout;
  113. } else {
  114. upgradeWell.textContent += obj.data.last_yum_command + "\n" + obj.data.stdout;
  115. }
  116. // Ok, now kick off actual install TODO use WebSocket?
  117. upgradeWell.textContent += "\nNow proceeding with install of PostgreSQL " + window.selectedVersion + "...\n";
  118. doAPIRequestWithCallback( 'POST', 'Postgres', 'start_postgres_install', handlePGInstall, generalErrorHandler, { "version": window.selectedVersion } );
  119. } else {
  120. console.log(obj.error);
  121. upgradeWell.textContent += "Installlation of community repositories failed:" + obj.error;
  122. submitBtn.textContent = 'Re-Try';
  123. submitBtn.disabled = false;
  124. }
  125. return false;
  126. }
  127. function handlePGInstall (resp) {
  128. 'use strict';
  129. let obj = safeParseJSON(resp);
  130. let upgradeWell = document.getElementById('upgradeWell');
  131. let submitBtn = document.getElementById('submit');
  132. if(obj.result === 1) {
  133. upgradeWell.textContent += `Attaching to log file ${obj.data.log} from process #${obj.data.pid}...\n\n`;
  134. doAPIRequestWithCallback( 'GET', 'Postgres', 'get_latest_upgradelog_messages', roadRoller, generalErrorHandler, { "pid": obj.data.pid, "log": obj.data.log, "start": 0 } );
  135. } else {
  136. upgradeWell.textContent += `Installlation PostgreSQL ${window.selectedVersion} failed: ${obj.error}`;
  137. submitBtn.textContent = 'Re-Try';
  138. submitBtn.disabled = false;
  139. }
  140. return false;
  141. }
  142. // 8 seconds have passed
  143. function roadRoller (resp) {
  144. 'use strict';
  145. let obj = safeParseJSON(resp);
  146. let upgradeWell = document.getElementById('upgradeWell');
  147. let submitBtn = document.getElementById('submit');
  148. if(obj.result === 1) {
  149. // Paste in new content
  150. if(obj.data['new_content'].length > 0) {
  151. upgradeWell.textContent += obj.data['new_content'];
  152. upgradeWell.scrollTo(0,upgradeWell.scrollHeight);
  153. }
  154. if(obj.data['in_progress']) {
  155. // Not done yet, keep going
  156. doAPIRequestWithCallback(
  157. 'GET', 'Postgres', 'get_latest_upgradelog_messages', roadRoller, generalErrorHandler, {
  158. "pid": obj.metadata['input_args'].pid,
  159. "log": obj.metadata['input_args'].log,
  160. "start": obj.data['next']
  161. }
  162. );
  163. } else {
  164. // Do something based on the end status
  165. if(+obj.data['child_exit']) {
  166. upgradeWell.textContent += `Installation of PostgreSQL ${window.selectedVersion} failed: Subprocess exited ${obj.data['child_exit']}`;
  167. upgradeWell.scrollTo(0,upgradeWell.scrollHeight);
  168. submitBtn.textContent = 'Re-Try';
  169. submitBtn.disabled = false;
  170. return;
  171. }
  172. upgradeWell.textContent += `Installation of PostgreSQL ${window.selectedVersion} completed successfully!`;
  173. upgradeWell.scrollTo(0,upgradeWell.scrollHeight);
  174. submitBtn.textContent = 'All done, please refresh the page.';
  175. }
  176. } else {
  177. upgradeWell.textContent += `Installation of PostgreSQL ${window.selectedVersion} failed: ${obj.error}`;
  178. upgradeWell.scrollTo(0,upgradeWell.scrollHeight);
  179. submitBtn.textContent = 'Re-Try';
  180. submitBtn.disabled = false;
  181. }
  182. }
  183. window.doUpgrade = function () {
  184. 'use strict';
  185. let form = new FormData(upgradeForm);
  186. window.selectedVersion = form.get('selectedVersion');
  187. document.getElementById('upgradeTitle').textContent = "Install Progress for PostgreSQL " + window.selectedVersion;
  188. let submitBtn = document.getElementById('submit');
  189. submitBtn.disabled = true;
  190. submitBtn.innerHTML = '<span class="fa fa-spin fa-spinner"></span>';
  191. let upgradeWell = document.getElementById('upgradeWell');
  192. upgradeWell.textContent = 'Ensuring that the PostgreSQL Community repository is installed...\n';
  193. upgradeWell.style.display = "block";
  194. upgradeForm.style.display = "none";
  195. doAPIRequestWithCallback( 'GET', 'Postgres', 'enable_community_repositories', doInstallScroller, generalErrorHandler );
  196. return false;
  197. }
  198. // Now kickoff the page load post bits
  199. document.getElementById('submit').disabled = true;
  200. doAPIRequestWithCallback( 'GET', 'Postgres', 'get_postgresql_versions', versionHandler, generalErrorHandler );