From 0f777d3e6868c6ec09364f5206d6dff152ddb6d7 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Thu, 24 Jul 2025 15:01:04 +0700 Subject: [PATCH] added download certificate and key in zip format --- api/CertDownload.go | 47 +++++ api/frontend/index.html | 367 +++++++++++++++++---------------- api/frontend/js/certs.js | 4 +- api/frontend/js/create_cert.js | 73 +++---- api/frontend/js/rootcrt.js | 7 +- server/Configure.go | 1 + utils/AddFileToZipWithName.go | 21 ++ utils/CreateDomainZip.go | 26 +++ 8 files changed, 333 insertions(+), 213 deletions(-) create mode 100644 api/CertDownload.go create mode 100644 utils/AddFileToZipWithName.go create mode 100644 utils/CreateDomainZip.go diff --git a/api/CertDownload.go b/api/CertDownload.go new file mode 100644 index 0000000..0aecfc3 --- /dev/null +++ b/api/CertDownload.go @@ -0,0 +1,47 @@ +package api + +import ( + "fmt" + "net/http" + "os" + "path/filepath" + "scm/utils" +) + +func CertKeyDownloadZip(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + domain := query.Get("domain") + if domain == "" { + http.Error(w, "domain parameter is required", http.StatusBadRequest) + return + } + + // Пути к файлам сертификата и ключа + certPath := filepath.Join(os.Getenv("CERTS_PATH"), domain, domain+".crt") + keyPath := filepath.Join(os.Getenv("CERTS_PATH"), domain, domain+".key") + + // Проверяем существование файлов + if _, err := os.Stat(certPath); os.IsNotExist(err) { + http.Error(w, "Certificate file not found", http.StatusNotFound) + return + } + if _, err := os.Stat(keyPath); os.IsNotExist(err) { + http.Error(w, "Key file not found", http.StatusNotFound) + return + } + + // Создаем ZIP архив + zipBuffer, err := utils.CreateDomainZip(certPath, keyPath, domain) + if err != nil { + http.Error(w, fmt.Sprintf("Error creating zip: %v", err), http.StatusInternalServerError) + return + } + + // Устанавливаем заголовки для скачивания + w.Header().Set("Content-Type", "application/zip") + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s-cert-key.zip\"", domain)) + w.Header().Set("Content-Length", fmt.Sprintf("%d", zipBuffer.Len())) + + // Отправляем ZIP архив + w.Write(zipBuffer.Bytes()) +} diff --git a/api/frontend/index.html b/api/frontend/index.html index 15f0af2..c6fe309 100644 --- a/api/frontend/index.html +++ b/api/frontend/index.html @@ -62,13 +62,15 @@ @@ -91,71 +93,78 @@ > - @@ -315,114 +324,124 @@ - diff --git a/api/frontend/js/certs.js b/api/frontend/js/certs.js index 9577de4..8fdaa8e 100644 --- a/api/frontend/js/certs.js +++ b/api/frontend/js/certs.js @@ -34,9 +34,9 @@ const renderCards = () => { > Cert - + diff --git a/api/frontend/js/create_cert.js b/api/frontend/js/create_cert.js index 0fa45a8..434d486 100644 --- a/api/frontend/js/create_cert.js +++ b/api/frontend/js/create_cert.js @@ -3,42 +3,45 @@ const commonname = document.querySelector("#commonname"); const organizationname = document.querySelector("#organizationname"); const password = document.querySelector("#password"); const organizationunit = document.querySelector("#organizationunit"); -const createcert = document.querySelector("#createcert"); +const create_certificate_form = document.querySelector( + "#create_certificate_form", +); -createcert.addEventListener("click", () => { - const domainValue = domain.value; - const commonnameValue = commonname.value; - const organizationnameValue = organizationname.value; - const passwordValue = password.value; - const organizationunitValue = organizationunit.value; +create_certificate_form.addEventListener("submit", (event) => { + event.preventDefault(); + const domainValue = domain.value; + const commonnameValue = commonname.value; + const organizationnameValue = organizationname.value; + const passwordValue = password.value; + const organizationunitValue = organizationunit.value; - fetch(window.location.origin + "/api/certs/create", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - dns: [domainValue], - commonname: commonnameValue, - organizationname: [organizationnameValue], - organizationunit: [organizationunitValue], - password: passwordValue, - }), + fetch(window.location.origin + "/api/certs/create", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + dns: [domainValue], + commonname: commonnameValue, + organizationname: [organizationnameValue], + organizationunit: [organizationunitValue], + password: passwordValue, + }), + }) + .then((data) => data.json()) + .then((jsondata) => { + if (jsondata.status === "success") { + getCerts(); + var createmodal = bootstrap.Modal.getOrCreateInstance( + document.getElementById("create_certificate"), + ); + createmodal.hide(); + alert("Certificate created successfully"); + } else { + alert("Failed to create certificate:", jsondata.error); + } }) - .then((data) => data.json()) - .then((jsondata) => { - if (jsondata.status === "success") { - getCerts(); - var createmodal = bootstrap.Modal.getOrCreateInstance( - document.getElementById("create_certificate"), - ); - createmodal.hide(); - alert("Certificate created successfully"); - } else { - alert("Failed to create certificate:", jsondata.error); - } - }) - .catch((error) => { - alert("An error occurred:", error); - }); + .catch((error) => { + alert("An error occurred:", error); + }); }); diff --git a/api/frontend/js/rootcrt.js b/api/frontend/js/rootcrt.js index 3253782..6991cb4 100644 --- a/api/frontend/js/rootcrt.js +++ b/api/frontend/js/rootcrt.js @@ -1,7 +1,9 @@ const createRootCertificateModal = bootstrap.Modal.getOrCreateInstance( document.querySelector("#createRootCertificate"), ); -const createRootCrtButton = document.querySelector("#createRootCrt"); +const createRootCertificateForm = document.querySelector( + "#createRootCertificateForm", +); const rootcommonnameinput = document.querySelector("#rootcommonname"); const rootorganizationinput = document.querySelector("#rootorganizationname"); @@ -29,7 +31,8 @@ const getRootCrt = () => { getRootCrt(); -createRootCrtButton.addEventListener("click", () => { +createRootCertificateForm.addEventListener("submit", (event) => { + event.preventDefault(); const newRootCertificate = { CommonName: rootcommonnameinput.value, Organization: [rootorganizationinput.value], diff --git a/server/Configure.go b/server/Configure.go index 9f52926..59637b3 100644 --- a/server/Configure.go +++ b/server/Configure.go @@ -9,6 +9,7 @@ func Configure() { http.Handle("/", api.SpaHandler(http.FS(api.Frontend))) http.HandleFunc("/api/certs", api.Certs) http.HandleFunc("/api/certs/root", api.RootCert) + http.HandleFunc("/api/certs/download/", api.CertKeyDownloadZip) http.HandleFunc("/api/certs/create", api.CreateCert) http.HandleFunc("/api/certs/delete", api.DeleteCert) } diff --git a/utils/AddFileToZipWithName.go b/utils/AddFileToZipWithName.go new file mode 100644 index 0000000..06ee214 --- /dev/null +++ b/utils/AddFileToZipWithName.go @@ -0,0 +1,21 @@ +package utils + +import ( + "archive/zip" + "os" +) + +func AddFileToZipWithName(zipWriter *zip.Writer, filePath, fileName string) error { + data, err := os.ReadFile(filePath) + if err != nil { + return err + } + + f, err := zipWriter.Create(fileName) + if err != nil { + return err + } + + _, err = f.Write(data) + return err +} diff --git a/utils/CreateDomainZip.go b/utils/CreateDomainZip.go new file mode 100644 index 0000000..7415e83 --- /dev/null +++ b/utils/CreateDomainZip.go @@ -0,0 +1,26 @@ +package utils + +import ( + "archive/zip" + "bytes" +) + +func CreateDomainZip(certPath, keyPath, domain string) (*bytes.Buffer, error) { + buf := new(bytes.Buffer) + zipWriter := zip.NewWriter(buf) + defer zipWriter.Close() + + // Добавляем сертификат + err := AddFileToZipWithName(zipWriter, certPath, domain+".crt") + if err != nil { + return nil, err + } + + // Добавляем ключ + err = AddFileToZipWithName(zipWriter, keyPath, domain+".key") + if err != nil { + return nil, err + } + + return buf, nil +}