186 lines
6.3 KiB
Go
186 lines
6.3 KiB
Go
package plugins
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
)
|
|
|
|
type CatalogSettings struct {
|
|
IncludeApprovedCommunityPlugins bool
|
|
ApprovedCommunityPluginCount int
|
|
InstalledCommunityPluginCount int
|
|
MigratedPluginCount int
|
|
CommunityUpdatesPaused bool
|
|
}
|
|
|
|
type ManagedRepositoryReconcileResult struct {
|
|
RepositoriesCreated int
|
|
}
|
|
|
|
func (s *RepositoryStore) ReconcileManaged(ctx context.Context) (ManagedRepositoryReconcileResult, error) {
|
|
tx, err := s.pool.Begin(ctx)
|
|
if err != nil {
|
|
return ManagedRepositoryReconcileResult{}, fmt.Errorf("begin managed plugin repository reconciliation: %w", err)
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO server_settings (key, value)
|
|
VALUES ($1, 'false')
|
|
ON CONFLICT (key) DO NOTHING
|
|
`, IncludeApprovedCommunityPluginsSetting); err != nil {
|
|
return ManagedRepositoryReconcileResult{}, fmt.Errorf("seed approved community plugin setting: %w", err)
|
|
}
|
|
|
|
includeCommunity, err := readIncludeApprovedCommunityForUpdate(ctx, tx)
|
|
if err != nil {
|
|
return ManagedRepositoryReconcileResult{}, err
|
|
}
|
|
created, err := reconcileManagedRepositories(ctx, tx, includeCommunity)
|
|
if err != nil {
|
|
return ManagedRepositoryReconcileResult{}, err
|
|
}
|
|
|
|
if err := tx.Commit(ctx); err != nil {
|
|
return ManagedRepositoryReconcileResult{}, fmt.Errorf("commit managed plugin repository reconciliation: %w", err)
|
|
}
|
|
return ManagedRepositoryReconcileResult{RepositoriesCreated: created}, nil
|
|
}
|
|
|
|
func (s *RepositoryStore) GetCatalogSettings(ctx context.Context) (CatalogSettings, error) {
|
|
includeCommunity, err := readIncludeApprovedCommunity(ctx, s.pool)
|
|
if err != nil {
|
|
return CatalogSettings{}, err
|
|
}
|
|
|
|
var installedCommunityCount int
|
|
if err := s.pool.QueryRow(ctx, `
|
|
SELECT COUNT(*)
|
|
FROM plugin_installations AS installation
|
|
JOIN plugin_repositories AS repository ON repository.id = installation.repository_id
|
|
WHERE repository.source_kind = $1
|
|
`, RepositorySourceApprovedCommunity).Scan(&installedCommunityCount); err != nil {
|
|
return CatalogSettings{}, fmt.Errorf("count installed approved community plugins: %w", err)
|
|
}
|
|
|
|
migratedPluginCount, err := readIntegerSetting(ctx, s.pool, MigratedApprovedCommunityCountSetting)
|
|
if err != nil {
|
|
return CatalogSettings{}, err
|
|
}
|
|
|
|
return CatalogSettings{
|
|
IncludeApprovedCommunityPlugins: includeCommunity,
|
|
ApprovedCommunityPluginCount: len(approvedCommunityPluginIDs),
|
|
InstalledCommunityPluginCount: installedCommunityCount,
|
|
MigratedPluginCount: migratedPluginCount,
|
|
CommunityUpdatesPaused: !includeCommunity && installedCommunityCount > 0,
|
|
}, nil
|
|
}
|
|
|
|
func (s *RepositoryStore) SetIncludeApprovedCommunity(ctx context.Context, include bool) (CatalogSettings, error) {
|
|
tx, err := s.pool.Begin(ctx)
|
|
if err != nil {
|
|
return CatalogSettings{}, fmt.Errorf("begin approved community plugin setting update: %w", err)
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO server_settings (key, value)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value
|
|
`, IncludeApprovedCommunityPluginsSetting, strconv.FormatBool(include)); err != nil {
|
|
return CatalogSettings{}, fmt.Errorf("update approved community plugin setting: %w", err)
|
|
}
|
|
|
|
if _, err := reconcileManagedRepositories(ctx, tx, include); err != nil {
|
|
return CatalogSettings{}, err
|
|
}
|
|
if err := tx.Commit(ctx); err != nil {
|
|
return CatalogSettings{}, fmt.Errorf("commit approved community plugin setting update: %w", err)
|
|
}
|
|
|
|
return s.GetCatalogSettings(ctx)
|
|
}
|
|
|
|
type catalogSettingsQuerier interface {
|
|
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
|
|
}
|
|
|
|
type catalogSettingsExecutor interface {
|
|
catalogSettingsQuerier
|
|
Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)
|
|
}
|
|
|
|
func readIncludeApprovedCommunity(ctx context.Context, querier catalogSettingsQuerier) (bool, error) {
|
|
return readIncludeApprovedCommunityQuery(ctx, querier, `SELECT value FROM server_settings WHERE key = $1`)
|
|
}
|
|
|
|
func readIncludeApprovedCommunityForUpdate(ctx context.Context, querier catalogSettingsQuerier) (bool, error) {
|
|
return readIncludeApprovedCommunityQuery(ctx, querier, `SELECT value FROM server_settings WHERE key = $1 FOR UPDATE`)
|
|
}
|
|
|
|
func readIncludeApprovedCommunityQuery(ctx context.Context, querier catalogSettingsQuerier, query string) (bool, error) {
|
|
var value string
|
|
err := querier.QueryRow(ctx, query, IncludeApprovedCommunityPluginsSetting).Scan(&value)
|
|
if err != nil {
|
|
if err == pgx.ErrNoRows {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("read approved community plugin setting: %w", err)
|
|
}
|
|
parsed, err := strconv.ParseBool(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
return parsed, nil
|
|
}
|
|
|
|
func readIntegerSetting(ctx context.Context, querier catalogSettingsQuerier, key string) (int, error) {
|
|
var value string
|
|
err := querier.QueryRow(ctx, `SELECT value FROM server_settings WHERE key = $1`, key).Scan(&value)
|
|
if err != nil {
|
|
if err == pgx.ErrNoRows {
|
|
return 0, nil
|
|
}
|
|
return 0, fmt.Errorf("read plugin setting %q: %w", key, err)
|
|
}
|
|
parsed, err := strconv.Atoi(strings.TrimSpace(value))
|
|
if err != nil || parsed < 0 {
|
|
return 0, nil
|
|
}
|
|
return parsed, nil
|
|
}
|
|
|
|
func reconcileManagedRepositories(ctx context.Context, executor catalogSettingsExecutor, includeCommunity bool) (int, error) {
|
|
created := 0
|
|
for _, definition := range managedRepositoryDefinitions {
|
|
var exists bool
|
|
if err := executor.QueryRow(ctx, `SELECT EXISTS (SELECT 1 FROM plugin_repositories WHERE url = $1)`, definition.URL).Scan(&exists); err != nil {
|
|
return 0, fmt.Errorf("check managed plugin repository %q: %w", definition.Key, err)
|
|
}
|
|
|
|
enabled := definition.Key == OfficialRepositoryManagedKey || includeCommunity
|
|
if _, err := executor.Exec(ctx, `
|
|
INSERT INTO plugin_repositories (url, display_name, enabled, managed_key, source_kind)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
ON CONFLICT (url) DO UPDATE SET
|
|
display_name = EXCLUDED.display_name,
|
|
enabled = EXCLUDED.enabled,
|
|
managed_key = EXCLUDED.managed_key,
|
|
source_kind = EXCLUDED.source_kind,
|
|
updated_at = NOW()
|
|
`, definition.URL, definition.DisplayName, enabled, definition.Key, definition.SourceKind); err != nil {
|
|
return 0, fmt.Errorf("reconcile managed plugin repository %q: %w", definition.Key, err)
|
|
}
|
|
if !exists {
|
|
created++
|
|
}
|
|
}
|
|
return created, nil
|
|
}
|