Feat v0.2.8 team admin audit (#12)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m18s
CI/CD / test-sqlite (push) Successful in 2m40s
CI/CD / build-and-deploy (push) Successful in 1m43s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #12.
This commit is contained in:
2026-03-27 16:25:19 +00:00
committed by xcaliber
parent 82430a2cb2
commit babfe66721
12 changed files with 51 additions and 102 deletions

View File

@@ -170,10 +170,6 @@ type TeamStore interface {
// AddMemberReturningID inserts a team member and returns the row ID.
AddMemberReturningID(ctx context.Context, teamID, userID, role string) (string, error)
// HasPrivateProviderRequirement checks if a user belongs to any active team
// with the require_private_providers setting enabled.
HasPrivateProviderRequirement(ctx context.Context, userID string) (bool, error)
// MergeSettings merges a JSON string into the team's settings column.
MergeSettings(ctx context.Context, teamID, settingsJSON string) error
}

View File

@@ -19,8 +19,7 @@ func SetDB(db *sql.DB) {
}
// ── Dynamic SQL Builder ─────────────────────
// Replaces the copy-pasted addClause/addField pattern
// found in admin.go, personas.go, team_providers.go, apiconfigs.go.
// Replaces the copy-pasted addClause/addField pattern.
// UpdateBuilder constructs a dynamic UPDATE statement.
type UpdateBuilder struct {

View File

@@ -142,7 +142,7 @@ func (s *TeamStore) UpdateMemberRole(ctx context.Context, teamID, userID, role s
func (s *TeamStore) ListMembers(ctx context.Context, teamID string) ([]models.TeamMember, error) {
rows, err := DB.QueryContext(ctx, `
SELECT tm.id, tm.team_id, tm.user_id, tm.role, tm.joined_at,
u.email, COALESCE(u.display_name, ''), u.username, u.role as user_role
u.email, COALESCE(u.display_name, ''), u.username
FROM team_members tm
JOIN users u ON u.id = tm.user_id
WHERE tm.team_id = $1
@@ -156,7 +156,7 @@ func (s *TeamStore) ListMembers(ctx context.Context, teamID string) ([]models.Te
for rows.Next() {
var m models.TeamMember
err := rows.Scan(&m.ID, &m.TeamID, &m.UserID, &m.Role, &m.JoinedAt,
&m.Email, &m.DisplayName, &m.Username, &m.UserRole)
&m.Email, &m.DisplayName, &m.Username)
if err != nil {
return nil, err
}
@@ -169,12 +169,12 @@ func (s *TeamStore) GetMember(ctx context.Context, teamID, userID string) (*mode
var m models.TeamMember
err := DB.QueryRowContext(ctx, `
SELECT tm.id, tm.team_id, tm.user_id, tm.role, tm.joined_at,
u.email, COALESCE(u.display_name, ''), u.username, u.role as user_role
u.email, COALESCE(u.display_name, ''), u.username
FROM team_members tm
JOIN users u ON u.id = tm.user_id
WHERE tm.team_id = $1 AND tm.user_id = $2`, teamID, userID).Scan(
&m.ID, &m.TeamID, &m.UserID, &m.Role, &m.JoinedAt,
&m.Email, &m.DisplayName, &m.Username, &m.UserRole)
&m.Email, &m.DisplayName, &m.Username)
if err != nil {
return nil, err
}
@@ -302,19 +302,6 @@ func (s *TeamStore) AddMemberReturningID(ctx context.Context, teamID, userID, ro
return id, err
}
func (s *TeamStore) HasPrivateProviderRequirement(ctx context.Context, userID string) (bool, error) {
var has bool
err := DB.QueryRowContext(ctx, `
SELECT EXISTS(
SELECT 1 FROM team_members tm
JOIN teams t ON t.id = tm.team_id
WHERE tm.user_id = $1
AND t.is_active = true
AND t.settings->>'require_private_providers' = 'true'
)`, userID).Scan(&has)
return has, err
}
func (s *TeamStore) MergeSettings(ctx context.Context, teamID, settingsJSON string) error {
_, err := DB.ExecContext(ctx,
`UPDATE teams SET settings = COALESCE(settings, '{}'::jsonb) || $1::jsonb WHERE id = $2`,

View File

@@ -149,7 +149,7 @@ func (s *TeamStore) UpdateMemberRole(ctx context.Context, teamID, userID, role s
func (s *TeamStore) ListMembers(ctx context.Context, teamID string) ([]models.TeamMember, error) {
rows, err := DB.QueryContext(ctx, `
SELECT tm.id, tm.team_id, tm.user_id, tm.role, tm.joined_at,
u.email, COALESCE(u.display_name, ''), u.username, u.role as user_role
u.email, COALESCE(u.display_name, ''), u.username
FROM team_members tm
JOIN users u ON u.id = tm.user_id
WHERE tm.team_id = ?
@@ -163,7 +163,7 @@ func (s *TeamStore) ListMembers(ctx context.Context, teamID string) ([]models.Te
for rows.Next() {
var m models.TeamMember
err := rows.Scan(&m.ID, &m.TeamID, &m.UserID, &m.Role, &m.JoinedAt,
&m.Email, &m.DisplayName, &m.Username, &m.UserRole)
&m.Email, &m.DisplayName, &m.Username)
if err != nil {
return nil, err
}
@@ -176,12 +176,12 @@ func (s *TeamStore) GetMember(ctx context.Context, teamID, userID string) (*mode
var m models.TeamMember
err := DB.QueryRowContext(ctx, `
SELECT tm.id, tm.team_id, tm.user_id, tm.role, tm.joined_at,
u.email, COALESCE(u.display_name, ''), u.username, u.role as user_role
u.email, COALESCE(u.display_name, ''), u.username
FROM team_members tm
JOIN users u ON u.id = tm.user_id
WHERE tm.team_id = ? AND tm.user_id = ?`, teamID, userID).Scan(
&m.ID, &m.TeamID, &m.UserID, &m.Role, &m.JoinedAt,
&m.Email, &m.DisplayName, &m.Username, &m.UserRole)
&m.Email, &m.DisplayName, &m.Username)
if err != nil {
return nil, err
}
@@ -308,19 +308,6 @@ func (s *TeamStore) AddMemberReturningID(ctx context.Context, teamID, userID, ro
return id, err
}
func (s *TeamStore) HasPrivateProviderRequirement(ctx context.Context, userID string) (bool, error) {
var has bool
err := DB.QueryRowContext(ctx, `
SELECT EXISTS(
SELECT 1 FROM team_members tm
JOIN teams t ON t.id = tm.team_id
WHERE tm.user_id = ?
AND t.is_active = 1
AND json_extract(t.settings, '$.require_private_providers') = 'true'
)`, userID).Scan(&has)
return has, err
}
func (s *TeamStore) MergeSettings(ctx context.Context, teamID, settingsJSON string) error {
_, err := DB.ExecContext(ctx,
`UPDATE teams SET settings = json_patch(COALESCE(settings, '{}'), ?) WHERE id = ?`,