Skip to content

Commit

Permalink
✨ add HTTP API endpoints for online restore
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdd committed Feb 18, 2025
1 parent 80d07c1 commit 8bb971b
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ func NewHandler(
},
)
r.Get("/api/v1/backup/config", BackupConfigUsecase(authStorage, configStorage))

r.Post("/api/v1/restore/auth", RestoreAuthUsecase(authStorage))
r.Post("/api/v1/restore/logs", RestoreLogsUsecase(authStorage, logStorage))
r.Post("/api/v1/restore/config", RestoreConfigUsecase(authStorage, configStorage))
})

return service
Expand Down
65 changes: 65 additions & 0 deletions api/restore_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package api

import (
"context"
"log/slog"

"mime/multipart"

"github.com/swaggest/usecase"
"github.com/swaggest/usecase/status"

"link-society.com/flowg/internal/models"
apiUtils "link-society.com/flowg/internal/utils/api"

"link-society.com/flowg/internal/storage/auth"
)

type RestoreAuthRequest struct {
Backup multipart.File `formData:"backup"`
}

type RestoreAuthResponse struct {
Success bool `json:"success"`
}

func RestoreAuthUsecase(authStorage *auth.Storage) usecase.Interactor {
u := usecase.NewInteractor(
apiUtils.RequireScopeApiDecorator(
authStorage,
models.SCOPE_WRITE_ACLS,
func(
ctx context.Context,
req RestoreAuthRequest,
resp *RestoreAuthResponse,
) error {
defer req.Backup.Close()

err := authStorage.Restore(ctx, req.Backup)
if err != nil {
slog.ErrorContext(
ctx,
"Failed to restore authentication database",
slog.String("channel", "api"),
slog.String("error", err.Error()),
)

return status.Wrap(err, status.Internal)
}

resp.Success = true

return nil
},
),
)

u.SetName("restore_auth")
u.SetTitle("Restore Authentication Database")
u.SetDescription("Upload a full snapshot of the authentication database.")
u.SetTags("backup")

u.SetExpectedErrors(status.Unauthenticated, status.PermissionDenied, status.Internal)

return u
}
73 changes: 73 additions & 0 deletions api/restore_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package api

import (
"context"
"log/slog"

"mime/multipart"

"github.com/swaggest/usecase"
"github.com/swaggest/usecase/status"

"link-society.com/flowg/internal/models"
apiUtils "link-society.com/flowg/internal/utils/api"

"link-society.com/flowg/internal/storage/auth"
"link-society.com/flowg/internal/storage/config"
)

type RestoreConfigRequest struct {
Backup multipart.File `formData:"backup"`
}

type RestoreConfigResponse struct {
Success bool `json:"success"`
}

func RestoreConfigUsecase(
authStorage *auth.Storage,
configStorage *config.Storage,
) usecase.Interactor {
u := usecase.NewInteractor(
apiUtils.RequireScopesApiDecorator(
authStorage,
[]models.Scope{
models.SCOPE_WRITE_PIPELINES,
models.SCOPE_WRITE_TRANSFORMERS,
models.SCOPE_WRITE_ALERTS,
},
func(
ctx context.Context,
req RestoreConfigRequest,
resp *RestoreConfigResponse,
) error {
defer req.Backup.Close()

err := configStorage.Restore(ctx, req.Backup)
if err != nil {
slog.ErrorContext(
ctx,
"Failed to restore configuration database",
slog.String("channel", "api"),
slog.String("error", err.Error()),
)

return status.Wrap(err, status.Internal)
}

resp.Success = true

return nil
},
),
)

u.SetName("restore_config")
u.SetTitle("Restore Configuration")
u.SetDescription("Upload a full snapshot of the configuration database.")
u.SetTags("backup")

u.SetExpectedErrors(status.Unauthenticated, status.PermissionDenied, status.Internal)

return u
}
67 changes: 67 additions & 0 deletions api/restore_logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package api

import (
"context"
"log/slog"

"mime/multipart"

"github.com/swaggest/usecase"
"github.com/swaggest/usecase/status"

"link-society.com/flowg/internal/models"
apiUtils "link-society.com/flowg/internal/utils/api"

"link-society.com/flowg/internal/storage/auth"
"link-society.com/flowg/internal/storage/log"
)

type RestoreLogsRequest struct {
Backup multipart.File `json:"backup"`
}

type RestoreLogsResponse struct {
Success bool `json:"success"`
}

func RestoreLogsUsecase(
authStorage *auth.Storage,
logStorage *log.Storage,
) usecase.Interactor {
u := usecase.NewInteractor(
apiUtils.RequireScopeApiDecorator(
authStorage,
models.SCOPE_WRITE_STREAMS,
func(
ctx context.Context,
req RestoreLogsRequest,
resp *RestoreLogsResponse,
) error {
defer req.Backup.Close()

err := logStorage.Restore(ctx, req.Backup)
if err != nil {
slog.ErrorContext(
ctx,
"Failed to restore logs database",
slog.String("channel", "api"),
slog.String("error", err.Error()),
)

return status.Wrap(err, status.Internal)
}

return nil
},
),
)

u.SetName("restore_logs")
u.SetTitle("Restore Logs Database")
u.SetDescription("Upload a full snapshot of the logs database.")
u.SetTags("backup")

u.SetExpectedErrors(status.Unauthenticated, status.PermissionDenied, status.Internal)

return u
}

0 comments on commit 8bb971b

Please # to comment.