Files
goclaw/internal/store/pg/config_secrets.go
T
Viet TranandClaude Opus 4.6 f3f4c67b36 Initial commit: GoClaw AI agent gateway
Multi-agent AI gateway with WebSocket RPC, HTTP API, and messaging channel integrations.
Go port of OpenClaw with multi-tenant PostgreSQL, per-user isolation, security hardening,
and production observability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:58:07 +07:00

94 lines
2.3 KiB
Go

package pg
import (
"context"
"database/sql"
"fmt"
"log/slog"
"time"
"github.com/nextlevelbuilder/goclaw/internal/crypto"
)
// PGConfigSecretsStore implements store.ConfigSecretsStore backed by Postgres.
type PGConfigSecretsStore struct {
db *sql.DB
encKey string
}
func NewPGConfigSecretsStore(db *sql.DB, encryptionKey string) *PGConfigSecretsStore {
return &PGConfigSecretsStore{db: db, encKey: encryptionKey}
}
func (s *PGConfigSecretsStore) Get(ctx context.Context, key string) (string, error) {
var value []byte
err := s.db.QueryRowContext(ctx,
`SELECT value FROM config_secrets WHERE key = $1`, key).Scan(&value)
if err != nil {
return "", err
}
if len(value) > 0 && s.encKey != "" {
decrypted, err := crypto.Decrypt(string(value), s.encKey)
if err != nil {
return "", fmt.Errorf("decrypt secret %q: %w", key, err)
}
return decrypted, nil
}
return string(value), nil
}
func (s *PGConfigSecretsStore) Set(ctx context.Context, key, value string) error {
var stored []byte
if s.encKey != "" {
encrypted, err := crypto.Encrypt(value, s.encKey)
if err != nil {
return fmt.Errorf("encrypt secret %q: %w", key, err)
}
stored = []byte(encrypted)
} else {
stored = []byte(value)
}
_, err := s.db.ExecContext(ctx,
`INSERT INTO config_secrets (key, value, updated_at) VALUES ($1, $2, $3)
ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = $3`,
key, stored, time.Now(),
)
return err
}
func (s *PGConfigSecretsStore) Delete(ctx context.Context, key string) error {
_, err := s.db.ExecContext(ctx, `DELETE FROM config_secrets WHERE key = $1`, key)
return err
}
func (s *PGConfigSecretsStore) GetAll(ctx context.Context) (map[string]string, error) {
rows, err := s.db.QueryContext(ctx, `SELECT key, value FROM config_secrets`)
if err != nil {
return nil, err
}
defer rows.Close()
result := make(map[string]string)
for rows.Next() {
var key string
var value []byte
if err := rows.Scan(&key, &value); err != nil {
continue
}
if len(value) > 0 && s.encKey != "" {
decrypted, err := crypto.Decrypt(string(value), s.encKey)
if err != nil {
slog.Warn("config_secrets: failed to decrypt", "key", key, "error", err)
continue
}
result[key] = decrypted
} else {
result[key] = string(value)
}
}
return result, nil
}