From f4bac6b17e028ea8b7ef2d2b6f720a7546a2c830 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Wed, 11 Sep 2024 15:57:50 -0300 Subject: [PATCH 01/79] refact: change Go text lib to direct dependency as recommendation from Go itself --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 265d837fc..7da6c9f98 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.16.0 golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect google.golang.org/protobuf v1.34.2 // indirect From 94edb5f2931050bab1a076888b8586b6104fa3c8 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Wed, 11 Sep 2024 16:57:54 -0300 Subject: [PATCH 02/79] feat: add transientDbSvc and Ssl page initial structure --- src/presentation/http.go | 2 +- src/presentation/ui/page/ssls.templ | 38 ++++++++++++++++++ .../ui/presenter/sslsPresenter.go | 40 +++++++++++++++++++ src/presentation/ui/router.go | 13 ++++++ src/presentation/ui/ui.go | 3 +- 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 src/presentation/ui/page/ssls.templ create mode 100644 src/presentation/ui/presenter/sslsPresenter.go diff --git a/src/presentation/http.go b/src/presentation/http.go index 066e29cdd..0f501d1fd 100644 --- a/src/presentation/http.go +++ b/src/presentation/http.go @@ -31,7 +31,7 @@ func HttpServerInit( e := echo.New() api.ApiInit(e, persistentDbSvc, transientDbSvc, trailDbSvc) - ui.UiInit(e, persistentDbSvc) + ui.UiInit(e, persistentDbSvc, transientDbSvc) httpServer := http.Server{Addr: ":1618", Handler: e} diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ new file mode 100644 index 000000000..4e89995b9 --- /dev/null +++ b/src/presentation/ui/page/ssls.templ @@ -0,0 +1,38 @@ +package page + +import ( + "github.com/speedianet/os/src/domain/entity" + componentForm "github.com/speedianet/os/src/presentation/ui/component/form" + componentStructural "github.com/speedianet/os/src/presentation/ui/component/structural" +) + +script SslsIndexLocalState() { + document.addEventListener('alpine:init', () => { + Alpine.data('ssls', () => ({}) + }) +} + +templ SslsIndex(sslPairs []entity.SslPair) { + @SslsIndexLocalState() +
+
+
+ @componentStructural.PageTitle( + "Ssls", + "", + "ph-lock", + ) +
+
+ @componentForm.SubmitButton("create virtual host", "ph-plus-square", "", false) + @componentForm.SubmitButton("create mapping", "ph-plus-square", "", false) +
+
+
+ @SslsFormTable(sslPairs) +
+
+} + +templ SslsFormTable(sslPairs []entity.SslPair) { +} diff --git a/src/presentation/ui/presenter/sslsPresenter.go b/src/presentation/ui/presenter/sslsPresenter.go new file mode 100644 index 000000000..2eb9c9915 --- /dev/null +++ b/src/presentation/ui/presenter/sslsPresenter.go @@ -0,0 +1,40 @@ +package presenter + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "github.com/speedianet/os/src/domain/entity" + internalDbInfra "github.com/speedianet/os/src/infra/internalDatabase" + "github.com/speedianet/os/src/presentation/service" + uiHelper "github.com/speedianet/os/src/presentation/ui/helper" + "github.com/speedianet/os/src/presentation/ui/page" +) + +type SslsPresenter struct { + sslService *service.SslService +} + +func NewSslsPresenter( + persistentDbSvc *internalDbInfra.PersistentDatabaseService, + transientDbSvc *internalDbInfra.TransientDatabaseService, +) *SslsPresenter { + return &SslsPresenter{ + sslService: service.NewSslService(persistentDbSvc, transientDbSvc), + } +} + +func (presenter *SslsPresenter) Handler(c echo.Context) error { + responseOutput := presenter.sslService.Read() + if responseOutput.Status != service.Success { + return nil + } + + sslPairs, assertOk := responseOutput.Body.([]entity.SslPair) + if !assertOk { + return nil + } + + pageContent := page.SslsIndex(sslPairs) + return uiHelper.Render(c, pageContent, http.StatusOK) +} diff --git a/src/presentation/ui/router.go b/src/presentation/ui/router.go index a2318af95..db49cb65a 100644 --- a/src/presentation/ui/router.go +++ b/src/presentation/ui/router.go @@ -19,15 +19,18 @@ import ( type Router struct { baseRoute *echo.Group persistentDbSvc *internalDbInfra.PersistentDatabaseService + transientDbSvc *internalDbInfra.TransientDatabaseService } func NewRouter( baseRoute *echo.Group, persistentDbSvc *internalDbInfra.PersistentDatabaseService, + transientDbSvc *internalDbInfra.TransientDatabaseService, ) *Router { return &Router{ baseRoute: baseRoute, persistentDbSvc: persistentDbSvc, + transientDbSvc: transientDbSvc, } } @@ -58,6 +61,15 @@ func (router *Router) mappingsRoutes() { mappingsGroup.GET("/", mappingsPresenter.Handler) } +func (router *Router) sslsRoutes() { + sslsGroup := router.baseRoute.Group("/ssls") + + sslsPresenter := presenter.NewSslsPresenter( + router.persistentDbSvc, router.transientDbSvc, + ) + sslsGroup.GET("/", sslsPresenter.Handler) +} + func (router *Router) devRoutes() { devGroup := router.baseRoute.Group("/dev") devGroup.GET("/hot-reload", func(c echo.Context) error { @@ -97,6 +109,7 @@ func (router *Router) previousDashboardRoute() { func (router *Router) RegisterRoutes() { router.assetsRoute() router.mappingsRoutes() + router.sslsRoutes() if isDevMode, _ := voHelper.InterfaceToBool(os.Getenv("DEV_MODE")); isDevMode { router.devRoutes() diff --git a/src/presentation/ui/ui.go b/src/presentation/ui/ui.go index 37566ce3e..224cdb121 100644 --- a/src/presentation/ui/ui.go +++ b/src/presentation/ui/ui.go @@ -9,12 +9,13 @@ import ( func UiInit( e *echo.Echo, persistentDbSvc *internalDbInfra.PersistentDatabaseService, + transientDbSvc *internalDbInfra.TransientDatabaseService, ) { basePath := "" baseRoute := e.Group(basePath) e.Use(uiMiddleware.Authentication()) - router := NewRouter(baseRoute, persistentDbSvc) + router := NewRouter(baseRoute, persistentDbSvc, transientDbSvc) router.RegisterRoutes() } From 37a10524373601a8faa5cf6edeb029333d5e7e95 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Fri, 13 Sep 2024 13:51:33 -0300 Subject: [PATCH 03/79] feat: add right page title and description --- src/presentation/ui/page/ssls.templ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 4e89995b9..003cd3290 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -18,8 +18,8 @@ templ SslsIndex(sslPairs []entity.SslPair) {
@componentStructural.PageTitle( - "Ssls", - "", + "SSL Certificate", + "Manage your SSL certificates with ease, adding new ones, replacing existing ones, viewing information about current certificates, and substituting them with self-signed certificates to secure your virtual hosts.", "ph-lock", )
From 632ae7092f46802de96994b7804a06a544894be9 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 00:59:29 -0300 Subject: [PATCH 04/79] fix: remove h-full from Tag component --- src/presentation/ui/component/structural/tag.templ | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/presentation/ui/component/structural/tag.templ b/src/presentation/ui/component/structural/tag.templ index 418af54b5..d459775b5 100644 --- a/src/presentation/ui/component/structural/tag.templ +++ b/src/presentation/ui/component/structural/tag.templ @@ -2,8 +2,8 @@ package componentStructural templ Tag(highlightedIcon, highlightedLabel, tagValue, tagColor string) { -
-
+
+
if highlightedIcon != "" { @@ -13,7 +13,7 @@ templ Tag(highlightedIcon, highlightedLabel, tagValue, tagColor string) { }
-
+
{ tagValue }
From 50dabd549678b5824b136c9ca8fb109d833a5426 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 00:59:53 -0300 Subject: [PATCH 05/79] feat: add orientation to CircularIconButton tooltip div to centralize it --- .../ui/component/structural/circularIconButton.templ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/presentation/ui/component/structural/circularIconButton.templ b/src/presentation/ui/component/structural/circularIconButton.templ index 14089d2b4..fa9577fd6 100644 --- a/src/presentation/ui/component/structural/circularIconButton.templ +++ b/src/presentation/ui/component/structural/circularIconButton.templ @@ -15,7 +15,7 @@ templ CircularIconButtonWithTooltip( if tooltipText != "" { -
+
{ tooltipText }
} From 55e2d8a8ae89604ac029c7e0c79fc8702d0e4028 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 01:01:07 -0300 Subject: [PATCH 06/79] feat: add SslsTable with all necessary ssl pairs data --- src/presentation/ui/page/ssls.templ | 50 ++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 003cd3290..f77c0e7b3 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -8,7 +8,7 @@ import ( script SslsIndexLocalState() { document.addEventListener('alpine:init', () => { - Alpine.data('ssls', () => ({}) + Alpine.data('ssls', () => ({})) }) } @@ -24,15 +24,55 @@ templ SslsIndex(sslPairs []entity.SslPair) { )
- @componentForm.SubmitButton("create virtual host", "ph-plus-square", "", false) - @componentForm.SubmitButton("create mapping", "ph-plus-square", "", false) + @componentForm.SubmitButton("Add/swap SSL certificate", "ph-plus-square", "", false)
- @SslsFormTable(sslPairs) + @SslsTable(sslPairs)
} -templ SslsFormTable(sslPairs []entity.SslPair) { +templ SslsTable(sslPairs []entity.SslPair) { +
+
+ + + + + + + + + + + + for _, sslPair := range sslPairs { + + + + + + + + } + +
Virtual Hosts HostnamesIssued AtExpires AtCertificate Authority
+ for _, vhostHostname := range sslPair.VirtualHostsHostnames { + @componentStructural.Tag("ph-network", "", vhostHostname.String(), "speedia-500") + } + { sslPair.Certificate.IssuedAt.GetDateOnly() }{ sslPair.Certificate.ExpiresAt.GetDateOnly() }{ sslPair.Certificate.CertificateAuthority.String() } +
+ @componentStructural.CircularIconButtonWithTooltip( + "ph-download-simple", "blue-900", "blue-700", "", + "download PEM files", "blue-500", + ) + @componentStructural.CircularIconButtonWithTooltip( + "ph-arrows-clockwise", "red-800", "red-600", "", + "swap to self-signed", "red-500", + ) +
+
+
+
} From 8f6d98917b97c7b707d26c69701c201ffec94a89 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 02:35:51 -0300 Subject: [PATCH 07/79] feat: add downloadPemFile js function to Alpine --- src/presentation/ui/page/ssls.templ | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index f77c0e7b3..6aa8745e4 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -8,7 +8,26 @@ import ( script SslsIndexLocalState() { document.addEventListener('alpine:init', () => { - Alpine.data('ssls', () => ({})) + Alpine.data('ssls', () => ({ + // Primary states + + // Auxiliary states + downloadPemFile(sslPairId, fileContent, fileExtension) { + const blobFile = new Blob([atob(fileContent)], { type: 'text/plain' }); + const blobFileUrlObject = window.URL.createObjectURL(blobFile); + const downloadPemFileElement = document.createElement('a'); + + downloadPemFileElement.href = blobFileUrlObject; + downloadPemFileElement.download = `${sslPairId}.${fileExtension}`; + document.body.appendChild(downloadPemFileElement); + + downloadPemFileElement.click(); + window.URL.revokeObjectURL(blobFileUrlObject); + document.body.removeChild(downloadPemFileElement); + }, + + // Modal states + })) }) } @@ -58,10 +77,10 @@ templ SslsTable(sslPairs []entity.SslPair) { { sslPair.Certificate.ExpiresAt.GetDateOnly() } { sslPair.Certificate.CertificateAuthority.String() } -
+
@componentStructural.CircularIconButtonWithTooltip( - "ph-download-simple", "blue-900", "blue-700", "", - "download PEM files", "blue-500", + "ph-file-magnifying-glass", "blue-900", "blue-700", "", + "view PEM files", "blue-500", ) @componentStructural.CircularIconButtonWithTooltip( "ph-arrows-clockwise", "red-800", "red-600", "", From 2ef6975a0e031fa6538e507e9eee014951478cb0 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 03:28:10 -0300 Subject: [PATCH 08/79] feat: add GetVhostHostnames ui shared function --- .../ui/helper/getVhostHostnames.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/presentation/ui/helper/getVhostHostnames.go diff --git a/src/presentation/ui/helper/getVhostHostnames.go b/src/presentation/ui/helper/getVhostHostnames.go new file mode 100644 index 000000000..8c3359592 --- /dev/null +++ b/src/presentation/ui/helper/getVhostHostnames.go @@ -0,0 +1,25 @@ +package uiHelper + +import ( + "github.com/speedianet/os/src/domain/dto" + "github.com/speedianet/os/src/domain/entity" +) + +func GetVhostHostnames(inputToGetVhostHostnames interface{}) []string { + vhostHostnames := []string{} + + switch typedInput := inputToGetVhostHostnames.(type) { + case []dto.VirtualHostWithMappings: + for _, vhostWithMappings := range typedInput { + vhostHostnames = append(vhostHostnames, vhostWithMappings.Hostname.String()) + } + case []entity.SslPair: + for _, sslPair := range typedInput { + for _, vhostHostname := range sslPair.VirtualHostsHostnames { + vhostHostnames = append(vhostHostnames, vhostHostname.String()) + } + } + } + + return vhostHostnames +} From 540d771402d0f8332d0204dd643f47388ab4b398 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 03:28:29 -0300 Subject: [PATCH 09/79] refact: using ui helper to get vhost hostnames in mappings page --- src/presentation/ui/page/mappings.templ | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/presentation/ui/page/mappings.templ b/src/presentation/ui/page/mappings.templ index 4a7435a81..0d9265aa8 100644 --- a/src/presentation/ui/page/mappings.templ +++ b/src/presentation/ui/page/mappings.templ @@ -6,6 +6,7 @@ import ( componentForm "github.com/speedianet/os/src/presentation/ui/component/form" componentMappings "github.com/speedianet/os/src/presentation/ui/component/mappings" componentStructural "github.com/speedianet/os/src/presentation/ui/component/structural" + uiHelper "github.com/speedianet/os/src/presentation/ui/helper" ) script MappingsIndexLocalState() { @@ -259,15 +260,6 @@ templ CreateVhostModal() { } } -func getVhostHostnames(vhostsWithMappings []dto.VirtualHostWithMappings) []string { - vhostHostnames := []string{} - for _, vhostWithMappings := range vhostsWithMappings { - vhostHostnames = append(vhostHostnames, vhostWithMappings.Hostname.String()) - } - - return vhostHostnames -} - func getOnlyServiceNames() []string { onlyServiceNames := []string{} for serviceName := range valueObject.NativeSvcNamesWithAliases { @@ -289,7 +281,7 @@ templ CreateMappingForm(vhostsWithMappings []dto.VirtualHostWithMappings) { @componentForm.InputFieldReadOnly("text", "hostname", "Hostname", "virtualHost.hostname", false) @componentForm.InputFieldWithPrefix("text", "path", "Source URL", "mapping.path", "vhostHostnameWithTrailingSlash", false)
From 5bb8b847ac95fb0e1ec754b9887db9167d9001f4 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 03:29:02 -0300 Subject: [PATCH 10/79] feat: add AddOrReplaceSslCertificate modal and form --- src/presentation/ui/page/ssls.templ | 75 +++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 6aa8745e4..3cbfa104f 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -4,14 +4,30 @@ import ( "github.com/speedianet/os/src/domain/entity" componentForm "github.com/speedianet/os/src/presentation/ui/component/form" componentStructural "github.com/speedianet/os/src/presentation/ui/component/structural" + uiHelper "github.com/speedianet/os/src/presentation/ui/helper" ) script SslsIndexLocalState() { document.addEventListener('alpine:init', () => { Alpine.data('ssls', () => ({ // Primary states + sslPair: {}, + resetPrimaryStates() { + this.sslPair = { + sslPairId: '', + virtualHostsHostnames: [], + certificate: '', + key: '', + } + }, + init() { + this.resetPrimaryStates(); + }, // Auxiliary states + get shouldDisableAddOrReplaceSslCertificateSubmitButton() { + return this.sslPair.virtualHostsHostnames.length == 0 || this.sslPair.certificate == '' || this.sslPair.key == '' + }, downloadPemFile(sslPairId, fileContent, fileExtension) { const blobFile = new Blob([atob(fileContent)], { type: 'text/plain' }); const blobFileUrlObject = window.URL.createObjectURL(blobFile); @@ -27,6 +43,15 @@ script SslsIndexLocalState() { }, // Modal states + isAddOrReplaceSslCertificateModalOpen: false, + openAddOrReplaceSslCertificateModal() { + this.resetPrimaryStates(); + + this.isAddOrReplaceSslCertificateModalOpen = true; + }, + closeAddOrReplaceSslCertificateModal() { + this.isAddOrReplaceSslCertificateModalOpen = false; + }, })) }) } @@ -43,11 +68,22 @@ templ SslsIndex(sslPairs []entity.SslPair) { )
- @componentForm.SubmitButton("Add/swap SSL certificate", "ph-plus-square", "", false) + @componentForm.SubmitButton("Add/swap SSL certificate", "ph-plus-square", "openAddOrReplaceSslCertificateModal()", false)
-
- @SslsTable(sslPairs) +
+
+ @SslsTable(sslPairs) +
+ @AddOrReplaceSslCertificateModal(uiHelper.GetVhostHostnames(sslPairs))
} @@ -95,3 +131,36 @@ templ SslsTable(sslPairs []entity.SslPair) {
} + +templ AddOrReplaceSslCertificateForm(vhostsHostnames []string) { +
+
+ @componentForm.SelectInput( + "virtualHosts", "Virtual Hosts Hostnames", + "sslPair.virtualHostsHostnames", vhostsHostnames, false, + ) +
+
+ @componentForm.TextArea("certificate", "Certificate", "sslPair.certificate", false) + @componentForm.TextArea("key", "Private Key", "sslPair.key", false) +
+ @componentForm.DeactivatableSubmitButton( + "Add/replace SSL certificate", "ph-check-fat", "closeAddOrReplaceSslCertificateModal()", + "shouldDisableAddOrReplaceSslCertificateSubmitButton", false, + ) +
+} + +templ AddOrReplaceSslCertificateModal(vhostsHostnames []string) { + @componentStructural.Modal( + "Add/replace SSL certificate", "isAddOrReplaceSslCertificateModalOpen", + "closeAddOrReplaceSslCertificateModal()", "", + ) { + @AddOrReplaceSslCertificateForm(vhostsHostnames) + } +} From a1986bc169bc7f217df58904eaf4453004c8a53d Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 03:30:26 -0300 Subject: [PATCH 11/79] fix: aim HTMX update page content to only SslsTables instead modals too --- src/presentation/ui/page/ssls.templ | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 3cbfa104f..c07d0a9f2 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -72,19 +72,17 @@ templ SslsIndex(sslPairs []entity.SslPair) {
-
- @SslsTable(sslPairs) -
- @AddOrReplaceSslCertificateModal(uiHelper.GetVhostHostnames(sslPairs)) + @SslsTable(sslPairs)
+ @AddOrReplaceSslCertificateModal(uiHelper.GetVhostHostnames(sslPairs)) } From f07e2b61c2837dfe2003e1f2340254f0bac14def Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 03:46:00 -0300 Subject: [PATCH 12/79] feat: transform certificate and key input to base64 before send to backend --- src/presentation/ui/page/ssls.templ | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index c07d0a9f2..6435947b5 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -50,6 +50,9 @@ script SslsIndexLocalState() { this.isAddOrReplaceSslCertificateModalOpen = true; }, closeAddOrReplaceSslCertificateModal() { + this.sslPair.certificate = btoa(this.sslPair.certificate); + this.sslPair.key = btoa(this.sslPair.key); + this.isAddOrReplaceSslCertificateModalOpen = false; }, })) From 78a6b7c09927cfa6cb80882e8016d35898eb82f8 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 04:10:12 -0300 Subject: [PATCH 13/79] feat: send only vhosts hostnames to ssl page by ssl presenter --- src/presentation/ui/presenter/sslsPresenter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/presentation/ui/presenter/sslsPresenter.go b/src/presentation/ui/presenter/sslsPresenter.go index 2eb9c9915..7c4335ad2 100644 --- a/src/presentation/ui/presenter/sslsPresenter.go +++ b/src/presentation/ui/presenter/sslsPresenter.go @@ -35,6 +35,6 @@ func (presenter *SslsPresenter) Handler(c echo.Context) error { return nil } - pageContent := page.SslsIndex(sslPairs) + pageContent := page.SslsIndex(sslPairs, uiHelper.GetVhostHostnames(sslPairs)) return uiHelper.Render(c, pageContent, http.StatusOK) } From fef4e3f6943a2ddfafd0f966632591144bc6a23d Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 04:11:00 -0300 Subject: [PATCH 14/79] feat: add remove vhosts hostnames modal and form, and include vhost hostnames as param sent by presenter instead to call more than one time --- src/presentation/ui/page/ssls.templ | 61 ++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 6435947b5..998cfb4c0 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -4,7 +4,6 @@ import ( "github.com/speedianet/os/src/domain/entity" componentForm "github.com/speedianet/os/src/presentation/ui/component/form" componentStructural "github.com/speedianet/os/src/presentation/ui/component/structural" - uiHelper "github.com/speedianet/os/src/presentation/ui/helper" ) script SslsIndexLocalState() { @@ -14,7 +13,7 @@ script SslsIndexLocalState() { sslPair: {}, resetPrimaryStates() { this.sslPair = { - sslPairId: '', + id: '', virtualHostsHostnames: [], certificate: '', key: '', @@ -28,6 +27,9 @@ script SslsIndexLocalState() { get shouldDisableAddOrReplaceSslCertificateSubmitButton() { return this.sslPair.virtualHostsHostnames.length == 0 || this.sslPair.certificate == '' || this.sslPair.key == '' }, + get shouldDisableRemoveVirtualHostsHostnamesSubmitButton() { + return this.sslPair.id == '' || this.sslPair.virtualHostsHostnames.length == 0 + }, downloadPemFile(sslPairId, fileContent, fileExtension) { const blobFile = new Blob([atob(fileContent)], { type: 'text/plain' }); const blobFileUrlObject = window.URL.createObjectURL(blobFile); @@ -43,6 +45,16 @@ script SslsIndexLocalState() { }, // Modal states + isRemoveVirtualHostsHostnamesModalOpen: false, + openRemoveVirtualHostsHostnamesModal(sslPairId) { + this.resetPrimaryStates(); + + this.sslPair.id = sslPairId; + this.isRemoveVirtualHostsHostnamesModalOpen = true; + }, + closeRemoveVirtualHostsHostnamesModal() { + this.isRemoveVirtualHostsHostnamesModalOpen = false; + }, isAddOrReplaceSslCertificateModalOpen: false, openAddOrReplaceSslCertificateModal() { this.resetPrimaryStates(); @@ -59,7 +71,7 @@ script SslsIndexLocalState() { }) } -templ SslsIndex(sslPairs []entity.SslPair) { +templ SslsIndex(sslPairs []entity.SslPair, vhostsHostnames []string) { @SslsIndexLocalState()
@@ -71,7 +83,10 @@ templ SslsIndex(sslPairs []entity.SslPair) { )
- @componentForm.SubmitButton("Add/swap SSL certificate", "ph-plus-square", "openAddOrReplaceSslCertificateModal()", false) + @componentForm.SubmitButton( + "Add/swap SSL certificate", "ph-plus-square", + "openAddOrReplaceSslCertificateModal()", false, + )
@SslsTable(sslPairs)
- @AddOrReplaceSslCertificateModal(uiHelper.GetVhostHostnames(sslPairs)) + @RemoveVirtualHostsHostnamesModal(vhostsHostnames) + @AddOrReplaceSslCertificateModal(vhostsHostnames) } @@ -119,6 +135,11 @@ templ SslsTable(sslPairs []entity.SslPair) { "ph-file-magnifying-glass", "blue-900", "blue-700", "", "view PEM files", "blue-500", ) + @componentStructural.CircularIconButtonWithTooltip( + "ph-network-slash", "red-800", "red-600", + "openRemoveVirtualHostsHostnamesModal('"+sslPair.Id.String()+"')", + "remove virtual hosts hostnames", "red-500", + ) @componentStructural.CircularIconButtonWithTooltip( "ph-arrows-clockwise", "red-800", "red-600", "", "swap to self-signed", "red-500", @@ -133,6 +154,36 @@ templ SslsTable(sslPairs []entity.SslPair) { } +templ RemoveVirtualHostsHostnamesForm(vhostsHostnames []string) { +
+
+ @componentForm.InputFieldReadOnly("text", "id", "Ssl Pair Id", "sslPair.id", false) + @componentForm.SelectInput( + "virtualHosts", "Virtual Hosts Hostnames", + "sslPair.virtualHostsHostnames", vhostsHostnames, false, + ) +
+ @componentForm.DeactivatableSubmitButton( + "Remove virtual hosts hostnames", "ph-check-fat", "closeRemoveVirtualHostsHostnamesModal()", + "shouldDisableRemoveVirtualHostsHostnamesSubmitButton", false, + ) +
+} + +templ RemoveVirtualHostsHostnamesModal(vhostsHostnames []string) { + @componentStructural.Modal( + "Add/replace SSL certificate", "isRemoveVirtualHostsHostnamesModalOpen", + "closeRemoveVirtualHostsHostnamesModal()", "", + ) { + @RemoveVirtualHostsHostnamesForm(vhostsHostnames) + } +} + templ AddOrReplaceSslCertificateForm(vhostsHostnames []string) {
Date: Mon, 16 Sep 2024 04:15:35 -0300 Subject: [PATCH 15/79] fix: change remove vhosts hostnames modal title --- src/presentation/ui/page/ssls.templ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 998cfb4c0..f2c79fb5e 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -177,7 +177,7 @@ templ RemoveVirtualHostsHostnamesForm(vhostsHostnames []string) { templ RemoveVirtualHostsHostnamesModal(vhostsHostnames []string) { @componentStructural.Modal( - "Add/replace SSL certificate", "isRemoveVirtualHostsHostnamesModalOpen", + "Remove virtual hosts hostnames", "isRemoveVirtualHostsHostnamesModalOpen", "closeRemoveVirtualHostsHostnamesModal()", "", ) { @RemoveVirtualHostsHostnamesForm(vhostsHostnames) From 415095f766d3a376e1f0db63effcf5e936c54714 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 05:23:09 -0300 Subject: [PATCH 16/79] feat: add id, color and hoverColor to customize SubmitButton --- src/presentation/ui/component/form/submitButton.templ | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/presentation/ui/component/form/submitButton.templ b/src/presentation/ui/component/form/submitButton.templ index 506b6785e..1691ad4b9 100644 --- a/src/presentation/ui/component/form/submitButton.templ +++ b/src/presentation/ui/component/form/submitButton.templ @@ -1,19 +1,22 @@ package componentForm templ SubmitButton( - label, icon, onClick string, + id, label, icon, color, hoverColor, onClick string, denseMode bool, ) { From 22741749b91a334bc37d4602479450fb8baedf5e Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 15:34:14 -0300 Subject: [PATCH 22/79] refact: implements id and icon style to Mappings submit buttons --- src/presentation/ui/page/mappings.templ | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/presentation/ui/page/mappings.templ b/src/presentation/ui/page/mappings.templ index 0aa749d81..f2e10dc0c 100644 --- a/src/presentation/ui/page/mappings.templ +++ b/src/presentation/ui/page/mappings.templ @@ -151,12 +151,12 @@ templ MappingsIndex(vhostsWithMappings []dto.VirtualHostWithMappings) {
@componentForm.SubmitButton( - "", "create virtual host", "ph-plus-square", "speedia-500", - "speedia-300", "openCreateVhostModal()", false, + "create-vhost-button", "create virtual host", "ph-plus-square", "duotone", + "speedia-500", "speedia-300", "openCreateVhostModal()", false, ) @componentForm.SubmitButton( - "", "create mapping", "ph-plus-square", "speedia-500", - "speedia-300", "openCreateMappingModal()", false, + "create-mapping-button", "create mapping", "ph-plus-square", "duotone", + "speedia-500", "speedia-300", "openCreateMappingModal()", false, )
@@ -228,7 +228,7 @@ templ MappingsFormTable(vhostsWithMappings []dto.VirtualHostWithMappings) { templ CreateVhostForm() {
Date: Mon, 16 Sep 2024 15:34:47 -0300 Subject: [PATCH 23/79] feat: add ViewPemFiles modal --- src/presentation/ui/page/ssls.templ | 64 +++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 8b6431b49..951d6d5f6 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -30,13 +30,20 @@ script SslsIndexLocalState() { get shouldDisableRemoveVirtualHostsHostnamesSubmitButton() { return this.sslPair.id == '' || this.sslPair.virtualHostsHostnames.length == 0 }, - downloadPemFile(sslPairId, fileContent, fileExtension) { - const blobFile = new Blob([atob(fileContent)], { type: 'text/plain' }); + downloadPemFile(isKeyFileContent) { + let pemFileContent = this.sslPair.certificate; + let fileExtension = 'crt'; + if (isKeyFileContent) { + pemFileContent = this.sslPair.key + fileExtension = 'key'; + } + + const blobFile = new Blob([pemFileContent], { type: 'text/plain' }); const blobFileUrlObject = window.URL.createObjectURL(blobFile); const downloadPemFileElement = document.createElement('a'); downloadPemFileElement.href = blobFileUrlObject; - downloadPemFileElement.download = `${sslPairId}.${fileExtension}`; + downloadPemFileElement.download = `${this.sslPair.id}.${fileExtension}`; document.body.appendChild(downloadPemFileElement); downloadPemFileElement.click(); @@ -85,6 +92,18 @@ script SslsIndexLocalState() { this.isAddOrReplaceSslCertificateModalOpen = false; }, + isViewPemFilesModalOpen: false, + openViewPemFilesModal(sslPairId, certificateContent, keyContent) { + this.resetPrimaryStates(); + + this.sslPair.id = sslPairId; + this.sslPair.certificate = certificateContent; + this.sslPair.key = keyContent; + this.isViewPemFilesModalOpen = true; + }, + closeViewPemFilesModal() { + this.isViewPemFilesModalOpen = false; + }, })) }) } @@ -102,8 +121,8 @@ templ SslsIndex(sslPairs []entity.SslPair, vhostsHostnames []string) {
@componentForm.SubmitButton( - "", "Add/swap SSL certificate", "ph-plus-square", - "speedia-500", "speedia-300", + "add-or-swap-ssl-certificate-button", "Add/swap SSL certificate", + "ph-plus-square", "duotone", "speedia-500", "speedia-300", "openAddOrReplaceSslCertificateModal()", false, )
@@ -120,6 +139,7 @@ templ SslsIndex(sslPairs []entity.SslPair, vhostsHostnames []string) { @SslsTable(sslPairs) @AddOrReplaceSslCertificateModal(vhostsHostnames) + @ViewPemFilesModal() @RemoveVirtualHostsHostnamesModal(vhostsHostnames) @SwapToSelfSignedModal() @@ -152,7 +172,8 @@ templ SslsTable(sslPairs []entity.SslPair) {
@componentStructural.CircularIconButtonWithTooltip( - "ph-file-magnifying-glass", "blue-900", "blue-700", "", + "ph-file-magnifying-glass", "blue-900", "blue-700", + "openViewPemFilesModal('"+sslPair.Id.String()+"', `"+sslPair.Certificate.CertificateContent.String()+"`, `"+sslPair.Key.String()+"`)", "view PEM files", "blue-500", ) @componentStructural.CircularIconButtonWithTooltip( @@ -208,6 +229,31 @@ templ AddOrReplaceSslCertificateModal(vhostsHostnames []string) { } } +templ ViewPemFilesModal() { + @componentStructural.Modal( + "PEM files", "isViewPemFilesModalOpen", + "closeViewPemFilesModal()", "", + ) { +
+ @componentForm.InputFieldReadOnly("text", "id", "Ssl Pair Id", "sslPair.id", false) +
+ @componentForm.TextAreaReadOnly("certificate", "Certificate", "sslPair.certificate", false) + @componentForm.SubmitButton( + "download-certificate-pem-file-button", "", "ph-download-simple", + "bold", "cyan-900", "cyan-800", "downloadPemFile(false)", false, + ) +
+
+ @componentForm.TextAreaReadOnly("key", "Private Key", "sslPair.key", false) + @componentForm.SubmitButton( + "download-key-pem-file-button", "", "ph-download-simple", + "bold", "cyan-900", "cyan-800", "downloadPemFile(true)", false, + ) +
+
+ } +} + templ RemoveVirtualHostsHostnamesForm(vhostsHostnames []string) {
@componentForm.SubmitButton( - "", "Cancel", "", "gray-500", + "cancel-swap-self-signed-button", "Cancel", "", "", "gray-500", "gray-400", "closeSwapToSelfSignedModal()", false, )
@componentForm.SubmitButton( - "swap-to-self-signed-button", "Yes, swap ssl certificate", "", "speedia-500", - "speedia-300", "swapToSelfSigned()", false, + "swap-to-self-signed-button", "Yes, swap ssl certificate", "", "", + "speedia-500", "speedia-300", "swapToSelfSigned()", false, )
From c2f132859e05c2240bf3517f698909d36d2eb7db Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 15:44:19 -0300 Subject: [PATCH 24/79] refact: add "form" as sufix to all form ids --- src/presentation/ui/page/ssls.templ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 951d6d5f6..806998ea0 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -198,7 +198,7 @@ templ SslsTable(sslPairs []entity.SslPair) { templ AddOrReplaceSslCertificateForm(vhostsHostnames []string) { Date: Mon, 16 Sep 2024 16:13:14 -0300 Subject: [PATCH 25/79] feat: add copyToClipboard to TextAreaReadOnly --- .../ui/component/form/textArea.templ | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/presentation/ui/component/form/textArea.templ b/src/presentation/ui/component/form/textArea.templ index 27a0c4a00..63d86837d 100644 --- a/src/presentation/ui/component/form/textArea.templ +++ b/src/presentation/ui/component/form/textArea.templ @@ -30,12 +30,22 @@ templ TextArea( } +func getTextAreaReadOnlyClientSideState(elementId string) string { + return `{ + copyToClipboard() { + const textInputElement = document.getElementById("` + elementId + `"); + textInputElement.select(); + navigator.clipboard.writeText(textInputElement.value); + } +}` +} + templ TextAreaReadOnly( id, label, bindValuePath string, denseMode bool, ) { -
+
+
+ +
if label != "" {
@componentForm.SubmitButton( - "create-vhost-button", "create virtual host", "ph-plus-square", "duotone", + "create-vhost-button", "create virtual host", "ph-plus-square", "speedia-500", "speedia-300", "openCreateVhostModal()", false, ) @componentForm.SubmitButton( - "create-mapping-button", "create mapping", "ph-plus-square", "duotone", + "create-mapping-button", "create mapping", "ph-plus-square", "speedia-500", "speedia-300", "openCreateMappingModal()", false, )
diff --git a/src/presentation/ui/page/ssls.templ b/src/presentation/ui/page/ssls.templ index 806998ea0..f87fd223c 100644 --- a/src/presentation/ui/page/ssls.templ +++ b/src/presentation/ui/page/ssls.templ @@ -122,7 +122,7 @@ templ SslsIndex(sslPairs []entity.SslPair, vhostsHostnames []string) {
@componentForm.SubmitButton( "add-or-swap-ssl-certificate-button", "Add/swap SSL certificate", - "ph-plus-square", "duotone", "speedia-500", "speedia-300", + "ph-plus-square", "speedia-500", "speedia-300", "openAddOrReplaceSslCertificateModal()", false, )
@@ -239,15 +239,15 @@ templ ViewPemFilesModal() {
@componentForm.TextAreaReadOnly("certificate", "Certificate", "sslPair.certificate", false) @componentForm.SubmitButton( - "download-certificate-pem-file-button", "", "ph-download-simple", - "bold", "cyan-900", "cyan-800", "downloadPemFile(false)", false, + "download-certificate-pem-file-button", "Download certificate", "ph-download-simple", + "cyan-900", "cyan-800", "downloadPemFile(false)", false, )
@componentForm.TextAreaReadOnly("key", "Private Key", "sslPair.key", false) @componentForm.SubmitButton( - "download-key-pem-file-button", "", "ph-download-simple", - "bold", "cyan-900", "cyan-800", "downloadPemFile(true)", false, + "download-key-pem-file-button", "Download private key", "ph-download-simple", + "cyan-900", "cyan-800", "downloadPemFile(true)", false, )
@@ -296,13 +296,13 @@ templ SwapToSelfSignedModal() {
@componentForm.SubmitButton( - "cancel-swap-self-signed-button", "Cancel", "", "", "gray-500", + "cancel-swap-self-signed-button", "Cancel", "", "gray-500", "gray-400", "closeSwapToSelfSignedModal()", false, )
@componentForm.SubmitButton( - "swap-to-self-signed-button", "Yes, swap ssl certificate", "", "", + "swap-to-self-signed-button", "Yes, swap ssl certificate", "", "speedia-500", "speedia-300", "swapToSelfSigned()", false, )
From 145edfbdbdbc2a25260de010847615088f4331a5 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 16:33:53 -0300 Subject: [PATCH 27/79] fix: using visibility in circular icon button tooltip instead opacity to avoid show tooltip with hover in opacity 0 tooltip div --- .../ui/component/structural/circularIconButton.templ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/presentation/ui/component/structural/circularIconButton.templ b/src/presentation/ui/component/structural/circularIconButton.templ index fa9577fd6..2ca48f118 100644 --- a/src/presentation/ui/component/structural/circularIconButton.templ +++ b/src/presentation/ui/component/structural/circularIconButton.templ @@ -15,7 +15,7 @@ templ CircularIconButtonWithTooltip( if tooltipText != "" { -
+
{ tooltipText }
} From e20db391fd075fee30dcfa0d455b79d0c248b023 Mon Sep 17 00:00:00 2001 From: Matheus Marques Polillo Date: Mon, 16 Sep 2024 20:33:38 -0300 Subject: [PATCH 28/79] fix: use script local state to define alpine data to TextAreaReadOnly --- .../ui/component/form/textArea.templ | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/presentation/ui/component/form/textArea.templ b/src/presentation/ui/component/form/textArea.templ index 63d86837d..be63bf408 100644 --- a/src/presentation/ui/component/form/textArea.templ +++ b/src/presentation/ui/component/form/textArea.templ @@ -30,14 +30,16 @@ templ TextArea(
} -func getTextAreaReadOnlyClientSideState(elementId string) string { - return `{ - copyToClipboard() { - const textInputElement = document.getElementById("` + elementId + `"); - textInputElement.select(); - navigator.clipboard.writeText(textInputElement.value); - } -}` +script TextAreaReadOnlyLocalState(elementId string) { + document.addEventListener('alpine:init', () => { + Alpine.data('textAreaReadOnly', () => ({ + copyToClipboard() { + const textInputElement = document.getElementById(elementId); + textInputElement.select(); + navigator.clipboard.writeText(textInputElement.value); + } + })) + }) } templ TextAreaReadOnly( @@ -45,7 +47,8 @@ templ TextAreaReadOnly( denseMode bool, ) { -
+ @TextAreaReadOnlyLocalState(id) +
if label != "" { @@ -57,14 +57,14 @@ templ TextAreaReadOnly( } placeholder={ label } if denseMode { - class="bg-os-300 border-os-200 hover:border-os-100 autofill:bg-os-300 focus:border-os-50 h-10 peer relative w-full rounded-md border px-1.5 pt-1.5 text-[13px] text-neutral-100 placeholder-transparent outline-none transition-all cursor-default text-slate-400" + class="bg-os-300 border-os-200 hover:border-os-100 autofill:bg-os-300 focus:border-os-50 peer relative h-10 w-full cursor-default rounded-md border px-1.5 pt-1.5 text-[13px] text-neutral-100 text-slate-400 placeholder-transparent outline-none transition-all" } else { - class="bg-os-300 border-os-200 hover:border-os-100 autofill:bg-os-300 focus:border-os-50 peer relative h-30 w-full rounded-md border px-3 pt-1.5 text-sm text-neutral-100 placeholder-transparent outline-none transition-all cursor-default text-slate-400" + class="bg-os-300 border-os-200 hover:border-os-100 autofill:bg-os-300 focus:border-os-50 h-30 peer relative w-full cursor-default rounded-md border px-3 pt-1.5 text-sm text-neutral-100 text-slate-400 placeholder-transparent outline-none transition-all" } readonly >
-
diff --git a/src/presentation/ui/component/structural/circularIconButton.templ b/src/presentation/ui/component/structural/circularIconButton.templ index 2eae2c6db..961ba5a80 100644 --- a/src/presentation/ui/component/structural/circularIconButton.templ +++ b/src/presentation/ui/component/structural/circularIconButton.templ @@ -4,7 +4,7 @@ templ CircularIconButtonWithTooltip( icon, defaultColor, hoverColor, onClick, tooltipText, tooltipColor string, ) { -
+