feat: include Francis data in Pocket ID backups (#1645)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
Alessandro (Ale) Segala
2026-08-05 19:57:38 +00:00
committed by GitHub
co-authored by Claude Elias Schneider
parent 140b5d3cb4
commit 08407ae564
13 changed files with 509 additions and 56 deletions
+17 -3
View File
@@ -6,9 +6,10 @@ import (
"io"
"os"
"github.com/spf13/cobra"
"github.com/pocket-id/pocket-id/backend/internal/bootstrap"
"github.com/pocket-id/pocket-id/backend/internal/service"
"github.com/spf13/cobra"
)
type exportFlags struct {
@@ -33,7 +34,7 @@ func init() {
// runExport orchestrates the export flow
func runExport(ctx context.Context, flags exportFlags) error {
db, _, err := bootstrap.NewDatabase(ctx)
db, pg, err := bootstrap.NewDatabase(ctx)
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
@@ -48,7 +49,20 @@ func runExport(ctx context.Context, flags exportFlags) error {
_ = storage.Close()
}()
exportService := service.NewExportService(db, storage)
// The actor host's data lives outside of the Pocket ID schema, so it's exported through Francis
providerOpts, err := bootstrap.ActorsProviderOptions(db, pg)
if err != nil {
return err
}
actorsProvider, err := bootstrap.NewActorsBackupProvider(ctx, providerOpts)
if err != nil {
return fmt.Errorf("failed to initialize the actor host's data provider: %w", err)
}
defer func() {
_ = actorsProvider.Close()
}()
exportService := service.NewExportService(db, storage, actorsProvider)
var w io.Writer
if flags.Path == "-" {
+12 -10
View File
@@ -3,7 +3,6 @@ package cmds
import (
"archive/zip"
"context"
"database/sql"
"errors"
"fmt"
"io"
@@ -83,14 +82,7 @@ func runImport(ctx context.Context, flags importFlags) error {
}
// The cluster admin talks to the same database as the actor host, so build its provider options the same way the host does
var sqliteDB *sql.DB
if pg == nil {
sqliteDB, err = db.DB()
if err != nil {
return fmt.Errorf("failed to get sql.DB connection: %w", err)
}
}
providerOpts, err := bootstrap.ActorsProviderOptions(pg, sqliteDB)
providerOpts, err := bootstrap.ActorsProviderOptions(db, pg)
if err != nil {
return err
}
@@ -124,8 +116,18 @@ func runImport(ctx context.Context, flags importFlags) error {
_ = storage.Close()
}()
// The actor host's data lives outside of the Pocket ID schema, so it's restored through Francis
// Restoring requires exclusive access to the cluster, which was acquired above
actorsProvider, err := bootstrap.NewActorsBackupProvider(importCtx, providerOpts)
if err != nil {
return fmt.Errorf("failed to initialize the actor host's data provider: %w", err)
}
defer func() {
_ = actorsProvider.Close()
}()
// Create the import service
importService := service.NewImportService(db, storage)
importService := service.NewImportService(db, storage, actorsProvider)
// Load from ZIP
err = importService.ImportFromZip(importCtx, &zipReader.Reader)