Implement account management features in API, including endpoints for listing accounts and updating account roles. Introduce moderator role with associated permissions, and refactor access control checks to accommodate staff roles. Update database schema to support new role constraints and enhance frontend navigation for staff access.
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / compose (push) Has been cancelled

This commit is contained in:
2026-07-19 02:32:15 +03:00
parent 4b889fb8a0
commit 1a5a39baf0
18 changed files with 726 additions and 74 deletions

View File

@@ -55,7 +55,7 @@ func (s *Store) UpsertDiscordAccount(ctx context.Context, account domain.Account
err = tx.QueryRow(ctx, `INSERT INTO accounts(id,discord_id,username,avatar_url,role,created_at)
VALUES($1,$2,$3,$4,$5,$6) ON CONFLICT(discord_id) DO UPDATE
SET username=excluded.username,avatar_url=excluded.avatar_url,
role=CASE WHEN accounts.role='admin' THEN accounts.role ELSE excluded.role END
role=CASE WHEN accounts.role IN ('admin','moderator') THEN accounts.role ELSE excluded.role END
RETURNING id,discord_id,username,avatar_url,role,created_at`,
account.ID, account.DiscordID, account.Username, account.AvatarURL, role, account.CreatedAt).
Scan(&account.ID, &account.DiscordID, &account.Username, &account.AvatarURL, &account.Role, &account.CreatedAt)
@@ -84,6 +84,31 @@ func (s *Store) UpsertDiscordAccount(ctx context.Context, account domain.Account
return account, p, tx.Commit(ctx)
}
func (s *Store) ListAccounts(ctx context.Context) ([]domain.Account, error) {
rows, err := s.pool.Query(ctx, `SELECT id,discord_id,username,avatar_url,role,created_at FROM accounts ORDER BY lower(username),id`)
if err != nil {
return nil, err
}
defer rows.Close()
accounts := make([]domain.Account, 0)
for rows.Next() {
var account domain.Account
if err := rows.Scan(&account.ID, &account.DiscordID, &account.Username, &account.AvatarURL, &account.Role, &account.CreatedAt); err != nil {
return nil, err
}
accounts = append(accounts, account)
}
return accounts, rows.Err()
}
func (s *Store) UpdateAccountRole(ctx context.Context, accountID string, role domain.GlobalRole) (domain.Account, error) {
var account domain.Account
err := s.pool.QueryRow(ctx, `UPDATE accounts SET role=$2 WHERE id=$1 AND role<>'admin'
RETURNING id,discord_id,username,avatar_url,role,created_at`, accountID, role).
Scan(&account.ID, &account.DiscordID, &account.Username, &account.AvatarURL, &account.Role, &account.CreatedAt)
return account, mapError(err)
}
func (s *Store) CreateSession(ctx context.Context, token, accountID string, expires time.Time) error {
_, err := s.pool.Exec(ctx, `INSERT INTO sessions(token_hash,account_id,expires_at) VALUES($1,$2,$3)`, HashToken(token), accountID, expires)
return err
@@ -331,6 +356,21 @@ func (s *Store) GetRoster(ctx context.Context, eventID string) (domain.RosterDra
return roster, mapError(err)
}
func (s *Store) ResetRoster(ctx context.Context, eventID string) error {
tx, err := s.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
if _, err = tx.Exec(ctx, `DELETE FROM event_rosters WHERE event_id=$1`, eventID); err != nil {
return err
}
if _, err = tx.Exec(ctx, `DELETE FROM teams WHERE event_id=$1`, eventID); err != nil {
return err
}
return tx.Commit(ctx)
}
func (s *Store) StartScrim(ctx context.Context, event domain.Event, expectedVersion int, series []domain.Series, tournament *domain.Tournament) error {
tx, err := s.pool.Begin(ctx)
if err != nil {