Add admin console access and platform pages (trust, users, keys, workloads, settings)

This commit is contained in:
Emil
2026-08-03 00:11:47 +03:00
parent 7fb1059401
commit e923627ce0
36 changed files with 1562 additions and 86 deletions
@@ -35,6 +35,7 @@ func (s stubRepo) SetVerified(context.Context, uuid.UUID, bool) error {
func (s stubRepo) SetRole(context.Context, uuid.UUID, domain.Role) error {
return usecase.ErrUserNotFound
}
func (s stubRepo) ListUsers(context.Context) ([]*domain.User, error) { return nil, nil }
type stubHasher struct {
hashErr error
@@ -28,6 +28,10 @@ type UserRepository interface {
// SetRole changes a user's role, returning ErrUserNotFound if no such user
// exists.
SetRole(ctx context.Context, id uuid.UUID, role domain.Role) error
// ListUsers returns every account, oldest first. Admin-only: used by the
// coordinator admin console; the response must never carry password hashes
// (the caller projects the entity).
ListUsers(ctx context.Context) ([]*domain.User, error)
}
// WorkerKeyRepository persists and looks up the long-lived worker keys a user
@@ -38,12 +42,18 @@ type WorkerKeyRepository interface {
Insert(ctx context.Context, k *domain.WorkerKey) error
// ListByUser returns a user's live (non-revoked) keys, newest first.
ListByUser(ctx context.Context, userID uuid.UUID) ([]*domain.WorkerKey, error)
// ListAll returns every key (revoked included), newest first. Admin-only:
// backs the coordinator admin console's key table.
ListAll(ctx context.Context) ([]*domain.WorkerKey, error)
// GetActiveByHash returns the non-revoked key with the given hash, or
// ErrWorkerKeyNotFound.
GetActiveByHash(ctx context.Context, tokenHash string) (*domain.WorkerKey, error)
// Revoke retires a key the user owns, returning ErrWorkerKeyNotFound when no
// live key with that id belongs to the user.
Revoke(ctx context.Context, id, userID uuid.UUID) error
// RevokeAny retires a key by id regardless of its owner. Admin-only; the
// coordinator admin console uses it to cut a key immediately.
RevokeAny(ctx context.Context, id uuid.UUID) error
// TouchLastUsed records a successful exchange. Best-effort: a failure here
// must not fail the exchange itself.
TouchLastUsed(ctx context.Context, id uuid.UUID) error
@@ -0,0 +1,21 @@
package usecase
import (
"context"
"github.com/emil28092005/SciMesh/coordinator/internal/userservice/domain"
)
// ListUsers returns every account for the coordinator admin console. The
// handler must project the entities so password hashes never leave the service.
type ListUsers struct {
users UserRepository
}
func NewListUsers(users UserRepository) *ListUsers {
return &ListUsers{users: users}
}
func (uc *ListUsers) Execute(ctx context.Context) ([]*domain.User, error) {
return uc.users.ListUsers(ctx)
}
@@ -47,6 +47,34 @@ func (uc *ListWorkerKeys) Execute(ctx context.Context, userID uuid.UUID) ([]*dom
return uc.keys.ListByUser(ctx, userID)
}
// ListWorkerKeysAll returns every key in the service, revoked included, for
// the coordinator admin console. Admin-only.
type ListWorkerKeysAll struct {
keys WorkerKeyRepository
}
func NewListWorkerKeysAll(keys WorkerKeyRepository) *ListWorkerKeysAll {
return &ListWorkerKeysAll{keys: keys}
}
func (uc *ListWorkerKeysAll) Execute(ctx context.Context) ([]*domain.WorkerKey, error) {
return uc.keys.ListAll(ctx)
}
// RevokeWorkerKeyAdmin retires any key, regardless of owner. Admin-only; used
// by the coordinator admin console when a key must be cut immediately.
type RevokeWorkerKeyAdmin struct {
keys WorkerKeyRepository
}
func NewRevokeWorkerKeyAdmin(keys WorkerKeyRepository) *RevokeWorkerKeyAdmin {
return &RevokeWorkerKeyAdmin{keys: keys}
}
func (uc *RevokeWorkerKeyAdmin) Execute(ctx context.Context, id uuid.UUID) error {
return uc.keys.RevokeAny(ctx, id)
}
// RevokeWorkerKey retires one of the caller's keys.
type RevokeWorkerKey struct {
keys WorkerKeyRepository
@@ -64,6 +64,24 @@ func (r *fakeKeyRepo) TouchLastUsed(_ context.Context, id uuid.UUID) error {
return nil
}
func (r *fakeKeyRepo) ListAll(_ context.Context) ([]*domain.WorkerKey, error) {
out := make([]*domain.WorkerKey, 0, len(r.byID))
for _, k := range r.byID {
out = append(out, k)
}
return out, nil
}
func (r *fakeKeyRepo) RevokeAny(_ context.Context, id uuid.UUID) error {
k, ok := r.byID[id]
if !ok || k.Revoked() {
return usecase.ErrWorkerKeyNotFound
}
now := time.Now()
k.RevokedAt = &now
return nil
}
func newKeyFixtures(t *testing.T) (*usecase.CreateWorkerKey, *usecase.ExchangeWorkerKey, *usecase.RevokeWorkerKey, *usecase.ListWorkerKeys, *fakeKeyRepo, *domain.User) {
t.Helper()
users := memstore.NewUserRepo()