Files
ocdp-go/backend/internal/adapter/output/persistence/postgres/registry_repository.go
Ivan087 7f238a3168 refactor: full-stack restructure with multi-tenancy, workspace management, and K8s diagnostics
- Add Workspace domain (entity, repository, service, handler, DTO)
- Add multi-tenant K8s client with tenant binding and quota management
- Add K8s diagnostics client (instance diagnostics)
- Add authorization middleware (authz package)
- Restructure frontend to feature-based architecture (features/)
- Add User Management page in configuration
- Add AccessDenied page and route guards
- Refactor shared components (form inputs, layout, UI)
- Update Tailwind config for new design system
- Add comprehensive documentation (docs/, tasks/, plans)
- Improve cluster service with better kubeconfig handling
- Add tests for crypto, config, helm client, tenant binding
2026-05-12 16:15:14 +08:00

193 lines
5.2 KiB
Go

package postgres
import (
"context"
"database/sql"
"fmt"
"time"
"github.com/google/uuid"
"github.com/ocdp/cluster-service/internal/domain/entity"
"github.com/ocdp/cluster-service/internal/domain/repository"
"github.com/ocdp/cluster-service/internal/pkg/crypto"
)
type RegistryRepository struct {
db *DB
encryptor crypto.Encryptor
}
func NewRegistryRepository(db *DB, encryptor crypto.Encryptor) repository.RegistryRepository {
return &RegistryRepository{db: db, encryptor: encryptor}
}
func (r *RegistryRepository) Create(ctx context.Context, registry *entity.Registry) error {
if registry.ID == "" {
registry.ID = uuid.New().String()
}
encryptedPassword, err := r.encryptor.Encrypt(registry.Password)
if err != nil {
return fmt.Errorf("failed to encrypt password: %w", err)
}
query := `
INSERT INTO registries (id, workspace_id, owner_id, visibility, name, url, description, username, password, insecure, created_at, updated_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)
`
_, err = r.db.conn.ExecContext(ctx, query,
registry.ID,
registry.WorkspaceID,
registry.OwnerID,
registry.Visibility,
registry.Name,
registry.URL,
registry.Description,
registry.Username,
encryptedPassword,
registry.Insecure,
registry.CreatedAt,
registry.UpdatedAt,
)
if err != nil {
return fmt.Errorf("failed to create registry: %w", err)
}
return nil
}
func (r *RegistryRepository) GetByID(ctx context.Context, id string) (*entity.Registry, error) {
return r.get(ctx, "id = $1", id)
}
func (r *RegistryRepository) GetByName(ctx context.Context, name string) (*entity.Registry, error) {
return r.get(ctx, "name = $1", name)
}
func (r *RegistryRepository) get(ctx context.Context, where string, arg interface{}) (*entity.Registry, error) {
query := fmt.Sprintf(`
SELECT id, workspace_id, owner_id, visibility, name, url, description, username, password, insecure, created_at, updated_at
FROM registries
WHERE %s
`, where)
rows, err := r.db.conn.QueryContext(ctx, query, arg)
if err != nil {
return nil, fmt.Errorf("failed to get registry: %w", err)
}
defer rows.Close()
if !rows.Next() {
return nil, entity.ErrRegistryNotFound
}
registry, err := r.scanRegistry(rows)
if err != nil {
return nil, err
}
return registry, nil
}
func (r *RegistryRepository) Update(ctx context.Context, registry *entity.Registry) error {
registry.UpdatedAt = time.Now()
encryptedPassword, err := r.encryptor.Encrypt(registry.Password)
if err != nil {
return fmt.Errorf("failed to encrypt password: %w", err)
}
query := `
UPDATE registries
SET workspace_id = $1, owner_id = $2, visibility = $3, name = $4, url = $5,
description = $6, username = $7, password = $8, insecure = $9, updated_at = $10
WHERE id = $11
`
result, err := r.db.conn.ExecContext(ctx, query,
registry.WorkspaceID,
registry.OwnerID,
registry.Visibility,
registry.Name,
registry.URL,
registry.Description,
registry.Username,
encryptedPassword,
registry.Insecure,
registry.UpdatedAt,
registry.ID,
)
if err != nil {
return fmt.Errorf("failed to update registry: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get affected rows: %w", err)
}
if rows == 0 {
return entity.ErrRegistryNotFound
}
return nil
}
func (r *RegistryRepository) Delete(ctx context.Context, id string) error {
result, err := r.db.conn.ExecContext(ctx, `DELETE FROM registries WHERE id = $1`, id)
if err != nil {
return fmt.Errorf("failed to delete registry: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get affected rows: %w", err)
}
if rows == 0 {
return entity.ErrRegistryNotFound
}
return nil
}
func (r *RegistryRepository) List(ctx context.Context) ([]*entity.Registry, error) {
query := `
SELECT id, workspace_id, owner_id, visibility, name, url, description, username, password, insecure, created_at, updated_at
FROM registries
ORDER BY created_at DESC
`
rows, err := r.db.conn.QueryContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("failed to list registries: %w", err)
}
defer rows.Close()
registries := make([]*entity.Registry, 0)
for rows.Next() {
registry, err := r.scanRegistry(rows)
if err != nil {
return nil, err
}
registries = append(registries, registry)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("rows iteration error: %w", err)
}
return registries, nil
}
type registryScanner interface {
Scan(dest ...interface{}) error
}
func (r *RegistryRepository) scanRegistry(scanner registryScanner) (*entity.Registry, error) {
registry := &entity.Registry{}
var encryptedPassword sql.NullString
err := scanner.Scan(
&registry.ID,
&registry.WorkspaceID,
&registry.OwnerID,
&registry.Visibility,
&registry.Name,
&registry.URL,
&registry.Description,
&registry.Username,
&encryptedPassword,
&registry.Insecure,
&registry.CreatedAt,
&registry.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to scan registry: %w", err)
}
registry.Password, err = decryptMaybe(r.encryptor, encryptedPassword.String)
if err != nil {
return nil, fmt.Errorf("failed to decrypt password: %w", err)
}
return registry, nil
}