cert.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <h3><i class="ui lock icon"></i> TLS / SSL Certificates</h3>
  2. <p>Setup TLS cert for different domains of your reverse proxy server names</p>
  3. <div class="ui divider"></div>
  4. <h4>Default Certificates</h4>
  5. <small>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</small></p>
  6. <table class="ui very basic celled table">
  7. <thead>
  8. <tr><th>Key Type</th>
  9. <th>Exists</th>
  10. </tr></thead>
  11. <tbody>
  12. <tr>
  13. <td><i class="globe icon"></i> Default Public Key</td>
  14. <td id="pubkeyExists"></td>
  15. </tr>
  16. <tr>
  17. <td><i class="lock icon"></i> Default Private Key</td>
  18. <td id="prikeyExists"></td>
  19. </tr>
  20. </tbody>
  21. </table>
  22. <button class="ui button" onclick="uploadPublicKey();"><i class="globe icon"></i> Upload Public Key</button>
  23. <button class="ui black button" onclick="uploadPrivateKey();"><i class="lock icon"></i> Upload Private Key</button>
  24. <div class="ui divider"></div>
  25. <h4>Sub-domain Certificates</h4>
  26. <p>Provide certificates for multiple domains reverse proxy</p>
  27. <div class="ui fluid form">
  28. <div class="three fields">
  29. <div class="field">
  30. <label>Server Name (Domain)</label>
  31. <input type="text" id="certdomain" placeholder="example.com / blog.example.com">
  32. </div>
  33. <div class="field">
  34. <label>Public Key</label>
  35. <input type="file" id="pubkeySelector" onchange="handleFileSelect(event, 'pub')">
  36. </div>
  37. <div class="field">
  38. <label>Private Key</label>
  39. <input type="file" id="prikeySelector" onchange="handleFileSelect(event, 'pri')">
  40. </div>
  41. </div>
  42. <button class="ui teal button" onclick="handleDomainUploadByKeypress();"><i class="ui upload icon"></i> Upload</button>
  43. </div>
  44. <div id="certUploadSuccMsg" class="ui green message" style="display:none;">
  45. <i class="ui checkmark icon"></i> Certificate for domain <span id="certUploadingDomain"></span> uploaded.
  46. </div>
  47. <br>
  48. <div>
  49. <table class="ui very basic celled table">
  50. <thead>
  51. <tr><th>Domain</th>
  52. <th>Last Update</th>
  53. <th>Remove</th>
  54. </tr></thead>
  55. <tbody id="certifiedDomainList">
  56. </tbody>
  57. </table>
  58. <button class="ui green basic button" onclick="initManagedDomainCertificateList();"><i class="refresh icon"></i> Refresh List</button>
  59. </div>
  60. <div class="ui message">
  61. <h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
  62. If you have 3rd or even 4th level subdomains like <code>blog.example.com</code> or <code>en.blog.example.com</code> ,
  63. depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
  64. If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
  65. </div>
  66. <script>
  67. var uploadPendingPublicKey = undefined;
  68. var uploadPendingPrivateKey = undefined;
  69. //Delete the certificate by its domain
  70. function deleteCertificate(domain){
  71. if (confirm("Confirm delete certificate for " + domain + " ?")){
  72. $.ajax({
  73. url: "/cert/delete",
  74. method: "POST",
  75. data: {domain: domain},
  76. success: function(data){
  77. if (data.error != undefined){
  78. alert(data.error);
  79. }else{
  80. initManagedDomainCertificateList();
  81. }
  82. }
  83. });
  84. }
  85. }
  86. //List the stored certificates
  87. function initManagedDomainCertificateList(){
  88. $("#certifiedDomainList").html("");
  89. $.get("/cert/list?date=true", function(data){
  90. if (data.error != undefined){
  91. alert(data.error);
  92. }else{
  93. data.forEach(entry => {
  94. $("#certifiedDomainList").append(`<tr>
  95. <td>${entry.Domain}</td>
  96. <td>${entry.LastModifiedDate}</td>
  97. <td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
  98. </tr>`);
  99. })
  100. }
  101. })
  102. }
  103. initManagedDomainCertificateList();
  104. function handleDomainUploadByKeypress(){
  105. handleDomainKeysUpload(function(){
  106. $("#certUploadingDomain").text($("#certdomain").val().trim());
  107. //After uploaded, reset the file selector
  108. document.getElementById('pubkeySelector').value = '';
  109. document.getElementById('prikeySelector').value = '';
  110. document.getElementById('certdomain').value = '';
  111. uploadPendingPublicKey = undefined;
  112. uploadPendingPrivateKey = undefined;
  113. //Show succ
  114. $("#certUploadSuccMsg").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
  115. initManagedDomainCertificateList();
  116. });
  117. }
  118. //Handle domain keys upload
  119. function handleDomainKeysUpload(callback=undefined){
  120. let domain = $("#certdomain").val();
  121. if (domain.trim() == ""){
  122. alert("Missing domain.");
  123. return;
  124. }
  125. if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
  126. const publicKeyForm = new FormData();
  127. publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey');
  128. const privateKeyForm = new FormData();
  129. privateKeyForm.append('file', uploadPendingPrivateKey, 'privateKey');
  130. const publicKeyRequest = new XMLHttpRequest();
  131. publicKeyRequest.open('POST', '/cert/upload?ktype=pub&domain=' + domain);
  132. publicKeyRequest.onreadystatechange = function() {
  133. if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
  134. if (publicKeyRequest.status !== 200) {
  135. alert('Error uploading public key: ' + publicKeyRequest.statusText);
  136. }
  137. if (callback != undefined){
  138. callback();
  139. }
  140. }
  141. };
  142. publicKeyRequest.send(publicKeyForm);
  143. const privateKeyRequest = new XMLHttpRequest();
  144. privateKeyRequest.open('POST', '/cert/upload?ktype=pri&domain=' + domain);
  145. privateKeyRequest.onreadystatechange = function() {
  146. if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
  147. if (privateKeyRequest.status !== 200) {
  148. alert('Error uploading private key: ' + privateKeyRequest.statusText);
  149. }
  150. if (callback != undefined){
  151. callback();
  152. }
  153. }
  154. };
  155. privateKeyRequest.send(privateKeyForm);
  156. } else {
  157. alert('One or both of the files is missing or not a file object');
  158. }
  159. }
  160. //Handlers for selecting domain based key pairs
  161. //ktype = {"pub" / "pri"}
  162. function handleFileSelect(event, ktype="pub") {
  163. const file = event.target.files[0];
  164. //const fileNameInput = document.getElementById('selected-file-name');
  165. if (ktype == "pub"){
  166. uploadPendingPublicKey = file;
  167. }else if (ktype == "pri"){
  168. uploadPendingPrivateKey = file;
  169. }
  170. //fileNameInput.value = file.name;
  171. }
  172. //Check if the default keypairs exists
  173. function initDefaultKeypairCheck(){
  174. $.get("/cert/checkDefault", function(data){
  175. let tick = `<i class="ui green checkmark icon"></i>`;
  176. let cross = `<i class="ui red times icon"></i>`;
  177. $("#pubkeyExists").html(data.DefaultPubExists?tick:cross);
  178. $("#prikeyExists").html(data.DefaultPriExists?tick:cross);
  179. });
  180. }
  181. initDefaultKeypairCheck();
  182. function uploadPrivateKey(){
  183. // create file input element
  184. const input = document.createElement('input');
  185. input.type = 'file';
  186. // add change listener to file input
  187. input.addEventListener('change', () => {
  188. // create form data object
  189. const formData = new FormData();
  190. // add selected file to form data
  191. formData.append('file', input.files[0]);
  192. // send form data to server
  193. fetch('/cert/upload?ktype=pri', {
  194. method: 'POST',
  195. body: formData
  196. })
  197. .then(response => {
  198. initDefaultKeypairCheck();
  199. if (response.ok) {
  200. alert('File upload successful!');
  201. } else {
  202. response.text().then(text => {
  203. alert(text);
  204. });
  205. //console.log(response.text());
  206. //alert('File upload failed!');
  207. }
  208. })
  209. .catch(error => {
  210. alert('An error occurred while uploading the file.');
  211. console.error(error);
  212. });
  213. });
  214. // click file input to open file selector
  215. input.click();
  216. }
  217. function uploadPublicKey() {
  218. // create file input element
  219. const input = document.createElement('input');
  220. input.type = 'file';
  221. // add change listener to file input
  222. input.addEventListener('change', () => {
  223. // create form data object
  224. const formData = new FormData();
  225. // add selected file to form data
  226. formData.append('file', input.files[0]);
  227. // send form data to server
  228. fetch('/cert/upload?ktype=pub', {
  229. method: 'POST',
  230. body: formData
  231. })
  232. .then(response => {
  233. if (response.ok) {
  234. alert('File upload successful!');
  235. initDefaultKeypairCheck();
  236. } else {
  237. response.text().then(text => {
  238. alert(text);
  239. });
  240. //console.log(response.text());
  241. //alert('File upload failed!');
  242. }
  243. })
  244. .catch(error => {
  245. alert('An error occurred while uploading the file.');
  246. console.error(error);
  247. });
  248. });
  249. // click file input to open file selector
  250. input.click();
  251. }
  252. </script>