package store import ( "context" "errors" "fmt" "time" "github.com/jackc/pgx/v5" ) // Account ist ein Mandant (Creator, Agentur, Marke oder Kanzlei als // eigene Organisation). Jeder Beitrag gehört genau einem Account. // Verified ist nur für Kanzlei-Accounts relevant: ob sie im öffentlichen // Kanzlei-Verzeichnis gelistet werden (siehe Migration 0004). type Account struct { ID string Name string Verified bool CreatedAt time.Time } // CreateAccount legt einen neuen Mandanten an (verified startet false — // jede Freigabe fürs Kanzlei-Verzeichnis ist eine bewusste Admin-Aktion). func (s *Store) CreateAccount(ctx context.Context, name string) (Account, error) { var a Account err := s.Pool.QueryRow(ctx, ` INSERT INTO account (name) VALUES ($1) RETURNING id, name, verified, created_at `, name).Scan(&a.ID, &a.Name, &a.Verified, &a.CreatedAt) if err != nil { return Account{}, fmt.Errorf("store: create account: %w", err) } return a, nil } // GetAccount liest einen Mandanten anhand seiner ID. func (s *Store) GetAccount(ctx context.Context, id string) (Account, error) { var a Account err := s.Pool.QueryRow(ctx, ` SELECT id, name, verified, created_at FROM account WHERE id = $1 `, id).Scan(&a.ID, &a.Name, &a.Verified, &a.CreatedAt) if errors.Is(err, pgx.ErrNoRows) { return Account{}, ErrNotFound } if err != nil { return Account{}, fmt.Errorf("store: get account: %w", err) } return a, nil } // ListAccounts liefert alle Mandanten, neueste zuerst — für den // Admin-Bereich (Accounts-Verwaltung). func (s *Store) ListAccounts(ctx context.Context) ([]Account, error) { rows, err := s.Pool.Query(ctx, ` SELECT id, name, verified, created_at FROM account ORDER BY created_at DESC `) if err != nil { return nil, fmt.Errorf("store: list accounts: %w", err) } defer rows.Close() var out []Account for rows.Next() { var a Account if err := rows.Scan(&a.ID, &a.Name, &a.Verified, &a.CreatedAt); err != nil { return nil, fmt.Errorf("store: scan account: %w", err) } out = append(out, a) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("store: list accounts: %w", err) } return out, nil } // ListVerifiedKanzleien liefert alle Accounts, die fürs öffentliche // Kanzlei-Verzeichnis freigegeben sind UND mindestens einen Nutzer der // Rolle "kanzlei" haben — verified allein reicht nicht, falls ein Admin // versehentlich einen Nicht-Kanzlei-Account markiert. func (s *Store) ListVerifiedKanzleien(ctx context.Context) ([]Account, error) { rows, err := s.Pool.Query(ctx, ` SELECT DISTINCT a.id, a.name, a.verified, a.created_at FROM account a JOIN app_user u ON u.account_id = a.id WHERE a.verified = true AND u.role = 'kanzlei' ORDER BY a.name `) if err != nil { return nil, fmt.Errorf("store: list verified kanzleien: %w", err) } defer rows.Close() var out []Account for rows.Next() { var a Account if err := rows.Scan(&a.ID, &a.Name, &a.Verified, &a.CreatedAt); err != nil { return nil, fmt.Errorf("store: scan account: %w", err) } out = append(out, a) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("store: list verified kanzleien: %w", err) } return out, nil } // SetAccountVerified setzt die Freigabe fürs Kanzlei-Verzeichnis. func (s *Store) SetAccountVerified(ctx context.Context, id string, verified bool) (Account, error) { var a Account err := s.Pool.QueryRow(ctx, ` UPDATE account SET verified = $2 WHERE id = $1 RETURNING id, name, verified, created_at `, id, verified).Scan(&a.ID, &a.Name, &a.Verified, &a.CreatedAt) if errors.Is(err, pgx.ErrNoRows) { return Account{}, ErrNotFound } if err != nil { return Account{}, fmt.Errorf("store: set account verified: %w", err) } return a, nil }