Fix download button to support local backups

This commit is contained in:
Luis Eduardo Jeréz Girón
2024-08-03 22:31:12 -06:00
parent cceb30b2fb
commit 8853b6f13e
5 changed files with 85 additions and 53 deletions

View File

@@ -1,33 +0,0 @@
package executions
import (
"context"
"fmt"
"time"
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
"github.com/google/uuid"
)
func (s *Service) GetExecutionDownloadLink(
ctx context.Context, executionID uuid.UUID,
) (string, error) {
data, err := s.dbgen.ExecutionsServiceGetDownloadLinkData(
ctx, dbgen.ExecutionsServiceGetDownloadLinkDataParams{
ExecutionID: executionID,
DecryptionKey: *s.env.PBW_ENCRYPTION_KEY,
},
)
if err != nil {
return "", err
}
if !data.Path.Valid {
return "", fmt.Errorf("execution has no file associated")
}
return s.ints.StorageClient.S3GetDownloadLink(
data.DecryptedAccessKey, data.DecryptedSecretKey, data.Region,
data.Endpoint, data.BucketName, data.Path.String, time.Hour*12,
)
}

View File

@@ -1,12 +0,0 @@
-- name: ExecutionsServiceGetDownloadLinkData :one
SELECT
executions.path AS path,
destinations.bucket_name AS bucket_name,
destinations.region AS region,
destinations.endpoint AS endpoint,
pgp_sym_decrypt(destinations.access_key, sqlc.arg('decryption_key')::TEXT) AS decrypted_access_key,
pgp_sym_decrypt(destinations.secret_key, sqlc.arg('decryption_key')::TEXT) AS decrypted_secret_key
FROM executions
JOIN backups ON backups.id = executions.backup_id
JOIN destinations ON destinations.id = backups.destination_id
WHERE executions.id = @execution_id;

View File

@@ -0,0 +1,47 @@
package executions
import (
"context"
"fmt"
"time"
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
"github.com/google/uuid"
)
// GetExecutionDownloadLinkOrPath returns a download link for the file associated
// with the given execution. If the execution is stored locally, the link will
// be a file path.
//
// Returns a boolean indicating if the file is locally stored and the download
// link/path.
func (s *Service) GetExecutionDownloadLinkOrPath(
ctx context.Context, executionID uuid.UUID,
) (bool, string, error) {
data, err := s.dbgen.ExecutionsServiceGetDownloadLinkOrPathData(
ctx, dbgen.ExecutionsServiceGetDownloadLinkOrPathDataParams{
ExecutionID: executionID,
DecryptionKey: *s.env.PBW_ENCRYPTION_KEY,
},
)
if err != nil {
return false, "", err
}
if !data.Path.Valid {
return false, "", fmt.Errorf("execution has no file associated")
}
if data.IsLocal {
return true, s.ints.StorageClient.LocalGetFullPath(data.Path.String), nil
}
link, err := s.ints.StorageClient.S3GetDownloadLink(
data.DecryptedAccessKey, data.DecryptedSecretKey, data.Region.String,
data.Endpoint.String, data.BucketName.String, data.Path.String, time.Hour*12,
)
if err != nil {
return false, "", err
}
return false, link, nil
}

View File

@@ -0,0 +1,24 @@
-- name: ExecutionsServiceGetDownloadLinkOrPathData :one
SELECT
executions.path AS path,
backups.is_local AS is_local,
destinations.bucket_name AS bucket_name,
destinations.region AS region,
destinations.endpoint AS endpoint,
destinations.endpoint as destination_endpoint,
(
CASE WHEN destinations.access_key IS NOT NULL
THEN pgp_sym_decrypt(destinations.access_key, sqlc.arg('decryption_key')::TEXT)
ELSE ''
END
) AS decrypted_access_key,
(
CASE WHEN destinations.secret_key IS NOT NULL
THEN pgp_sym_decrypt(destinations.secret_key, sqlc.arg('decryption_key')::TEXT)
ELSE ''
END
) AS decrypted_secret_key
FROM executions
INNER JOIN backups ON backups.id = executions.backup_id
LEFT JOIN destinations ON destinations.id = backups.destination_id
WHERE executions.id = @execution_id;

View File

@@ -1,11 +1,13 @@
package executions
import (
"net/http"
"path/filepath"
lucide "github.com/eduardolat/gomponents-lucide"
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
"github.com/eduardolat/pgbackweb/internal/util/timeutil"
"github.com/eduardolat/pgbackweb/internal/view/web/component"
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/maragudk/gomponents"
@@ -17,17 +19,21 @@ func (h *handlers) downloadExecutionHandler(c echo.Context) error {
executionID, err := uuid.Parse(c.Param("executionID"))
if err != nil {
return htmx.RespondToastError(c, err.Error())
return c.String(http.StatusBadRequest, err.Error())
}
link, err := h.servs.ExecutionsService.GetExecutionDownloadLink(
isLocal, link, err := h.servs.ExecutionsService.GetExecutionDownloadLinkOrPath(
ctx, executionID,
)
if err != nil {
return htmx.RespondToastError(c, err.Error())
return c.String(http.StatusInternalServerError, err.Error())
}
return htmx.RespondRedirect(c, link)
if isLocal {
return c.Attachment(link, filepath.Base(link))
}
return c.Redirect(http.StatusFound, link)
}
func showExecutionButton(
@@ -114,9 +120,9 @@ func showExecutionButton(
html.Div(
html.Class("flex justify-end items-center space-x-2"),
deleteExecutionButton(execution.ID),
html.Button(
htmx.HxGet("/dashboard/executions/"+execution.ID.String()+"/download"),
htmx.HxDisabledELT("this"),
html.A(
html.Href("/dashboard/executions/"+execution.ID.String()+"/download"),
html.Target("_blank"),
html.Class("btn btn-primary"),
component.SpanText("Download"),
lucide.Download(),