Skip to content

Commit

Permalink
Merge pull request #91 from MinaFoundation/feature/worker-heartbeat-a…
Browse files Browse the repository at this point in the history
…dmin

Feature/worker heartbeat admin
  • Loading branch information
iluxonchik authored Dec 19, 2024
2 parents 11dc467 + 1636067 commit 40890f3
Show file tree
Hide file tree
Showing 10 changed files with 877 additions and 6 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pgt-web-app",
"version": "0.1.20",
"version": "0.1.22",
"private": true,
"type": "module",
"scripts": {
Expand Down Expand Up @@ -29,13 +29,14 @@
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-hover-card": "^1.1.4",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.2.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.4",
"bree": "^9.2.4",
Expand Down
23 changes: 23 additions & 0 deletions src/app/admin/worker-heartbeats/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { WorkerHeartbeatsTable } from "@/components/admin/worker-heartbeats/WorkerHeartbeatsTable";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Worker Heartbeats | MEF Admin",
description: "Monitor background job statuses and heartbeats",
};

export default function WorkerHeartbeatsPage() {
return (
<div className="container mx-auto p-6 max-w-7xl">
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold">Worker Heartbeats</CardTitle>
</CardHeader>
<CardContent>
<WorkerHeartbeatsTable />
</CardContent>
</Card>
</div>
);
}
74 changes: 74 additions & 0 deletions src/app/api/admin/worker-heartbeats/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { getOrCreateUserFromRequest } from "@/lib/auth";
import { AdminService } from "@/services/AdminService";
import { ApiResponse } from "@/lib/api-response";
import { AppError } from "@/lib/errors";
import { AuthErrors } from "@/constants/errors";
import logger from "@/logging";
import { WorkerStatus } from "@prisma/client";

const adminService = new AdminService(prisma);
const DEFAULT_PAGE_SIZE = 25;

type SortField = 'createdAt' | 'lastHeartbeat' | 'status' | 'name';
type SortOrder = 'asc' | 'desc';

export async function GET(request: Request) {
try {
const user = await getOrCreateUserFromRequest(request);
if (!user) {
throw AppError.unauthorized(AuthErrors.UNAUTHORIZED);
}

const isAdmin = await adminService.checkAdminStatus(user.id, user.linkId);
if (!isAdmin) {
throw AppError.forbidden(AuthErrors.FORBIDDEN);
}

// Parse query parameters
const { searchParams } = new URL(request.url);
const page = Math.max(1, Number(searchParams.get('page')) || 1);
const pageSize = Math.max(1, Number(searchParams.get('pageSize')) || DEFAULT_PAGE_SIZE);
const sortField = (searchParams.get('sortField') as SortField) || 'createdAt';
const sortOrder = (searchParams.get('sortOrder') as SortOrder) || 'desc';

// Validate sort field
const validSortFields: SortField[] = ['createdAt', 'lastHeartbeat', 'status', 'name'];
if (!validSortFields.includes(sortField)) {
throw AppError.badRequest('Invalid sort field');
}

// Get total count for pagination
const totalCount = await prisma.workerHeartbeat.count();
const totalPages = Math.ceil(totalCount / pageSize);

// Fetch paginated and sorted data
const heartbeats = await prisma.workerHeartbeat.findMany({
take: pageSize,
skip: (page - 1) * pageSize,
orderBy: {
[sortField]: sortOrder,
},
});

logger.info(`Fetched ${heartbeats.length} worker heartbeats (page ${page}/${totalPages})`);

// Return paginated response
return ApiResponse.success({
data: heartbeats,
pagination: {
currentPage: page,
totalPages,
pageSize,
totalCount,
},
sort: {
field: sortField,
order: sortOrder,
},
});
} catch (error) {
return ApiResponse.error(error);
}
}
8 changes: 7 additions & 1 deletion src/components/AdminDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Button } from "@/components/ui/button"
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Users, MessageSquare, Coins, FileCheck, Vote } from 'lucide-react'
import { Users, MessageSquare, Coins, FileCheck, Vote, Activity } from 'lucide-react'
import Link from "next/link"

export function AdminDashboardComponent() {
Expand Down Expand Up @@ -36,6 +36,12 @@ export function AdminDashboardComponent() {
description: "Count Votes for a Funding Round..",
icon: <Vote className="h-5 w-5" />,
href: "/admin/votes"
},
{
title: "Worker Heartbeats",
description: "Monitor background job statuses",
icon: <Activity className="h-5 w-5" />,
href: "/admin/worker-heartbeats"
}
]

Expand Down
Loading

0 comments on commit 40890f3

Please sign in to comment.