Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.benchmark
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TARGET_URL=http://localhost:3000
BENCHMARK_TOKEN=test-token
197 changes: 192 additions & 5 deletions apps/web/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,195 @@
"use client";

import React, { useState, useEffect } from "react";

// Mock data types
type User = { id: string; name: string; role: string; status: string; joinDate: string; trustScore: number };
type Job = { id: string; title: string; status: string; flaggedReason?: string };
type Dispute = { id: string; jobId: string; status: string; client: string; freelancer: string };
type AuditLog = { id: string; action: string; adminId: string; timestamp: string };

export default function AdminPanelPage() {
const [activeTab, setActiveTab] = useState("dashboard");
const [users, setUsers] = useState<User[]>([]);
const [disputes, setDisputes] = useState<Dispute[]>([]);
const [flaggedJobs, setFlaggedJobs] = useState<Job[]>([]);

// Platform Controls
const [registrationsEnabled, setRegistrationsEnabled] = useState(true);
const [postingsEnabled, setPostingsEnabled] = useState(true);

useEffect(() => {
// Fetch mock data on load
setUsers([
{ id: "1", name: "Alice", role: "freelancer", status: "active", joinDate: "2026-01-01", trustScore: 98 },
{ id: "2", name: "Bob", role: "client", status: "suspended", joinDate: "2026-02-15", trustScore: 42 }
]);
setDisputes([
{ id: "d1", jobId: "j1", status: "open", client: "Bob", freelancer: "Alice" }
]);
setFlaggedJobs([
{ id: "j2", title: "Suspicious API Gig", status: "flagged", flaggedReason: "Spam keywords" }
]);
}, []);

const handleToggle = (setting: string, currentVal: boolean) => {
if(confirm(`Are you sure you want to ${currentVal ? "disable" : "enable"} ${setting}?`)) {
if(setting === "registrations") setRegistrationsEnabled(!currentVal);
if(setting === "postings") setPostingsEnabled(!currentVal);
alert(`Audit Log: Toggled ${setting} to ${!currentVal}`);
}
};

return (
<section className="card">
<h2>Admin Panel</h2>
<p>Moderation queues, trust metrics, and platform controls are available here.</p>
</section>
<div className="p-8 max-w-7xl mx-auto space-y-6 text-gray-900 bg-gray-50 min-h-screen">
<header className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Platform Admin Panel</h1>
<div className="space-x-4">
<button onClick={() => setActiveTab("dashboard")} className={`px-4 py-2 rounded ${activeTab==="dashboard"?"bg-blue-600 text-white":"bg-gray-200"}`}>Dashboard</button>
<button onClick={() => setActiveTab("users")} className={`px-4 py-2 rounded ${activeTab==="users"?"bg-blue-600 text-white":"bg-gray-200"}`}>Users</button>
<button onClick={() => setActiveTab("moderation")} className={`px-4 py-2 rounded ${activeTab==="moderation"?"bg-blue-600 text-white":"bg-gray-200"}`}>Moderation</button>
<button onClick={() => setActiveTab("controls")} className={`px-4 py-2 rounded ${activeTab==="controls"?"bg-blue-600 text-white":"bg-gray-200"}`}>Controls</button>
</div>
</header>

{/* DASHBOARD */}
{activeTab === "dashboard" && (
<section className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="bg-white p-6 rounded shadow border-l-4 border-blue-500">
<h3 className="text-gray-500 text-sm">Total Users</h3>
<p className="text-3xl font-bold">{users.length}</p>
</div>
<div className="bg-white p-6 rounded shadow border-l-4 border-red-500">
<h3 className="text-gray-500 text-sm">Open Disputes</h3>
<p className="text-3xl font-bold">{disputes.length}</p>
</div>
<div className="bg-white p-6 rounded shadow border-l-4 border-yellow-500">
<h3 className="text-gray-500 text-sm">Flagged Jobs</h3>
<p className="text-3xl font-bold">{flaggedJobs.length}</p>
</div>
<div className="bg-white p-6 rounded shadow border-l-4 border-green-500">
<h3 className="text-gray-500 text-sm">Revenue (Period)</h3>
<p className="text-3xl font-bold">$12,450</p>
</div>
</section>
)}

{/* USERS */}
{activeTab === "users" && (
<section className="bg-white p-6 rounded shadow">
<h2 className="text-xl font-bold mb-4">User Management</h2>
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b">
<th className="py-2">Name</th>
<th>Role</th>
<th>Status</th>
<th>Trust Score</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map(u => (
<tr key={u.id} className="border-b">
<td className="py-2">{u.name}</td>
<td className="capitalize">{u.role}</td>
<td>
<span className={`px-2 py-1 text-xs rounded text-white ${u.status === 'active' ? 'bg-green-500' : 'bg-red-500'}`}>
{u.status}
</span>
</td>
<td>{u.trustScore}</td>
<td className="space-x-2">
<button className="text-sm text-blue-600 hover:underline">Profile</button>
<button className="text-sm text-red-600 hover:underline">Ban</button>
</td>
</tr>
))}
</tbody>
</table>
</section>
)}

{/* MODERATION */}
{activeTab === "moderation" && (
<div className="space-y-6">
<section className="bg-white p-6 rounded shadow">
<h2 className="text-xl font-bold mb-4 flex justify-between">
<span>Dispute Queue</span>
<span className="bg-red-100 text-red-700 px-3 py-1 rounded-full text-sm">{disputes.length} Open</span>
</h2>
{disputes.map(d => (
<div key={d.id} className="border p-4 rounded mb-2 flex justify-between items-center">
<div>
<p className="font-semibold">Dispute on Job {d.jobId}</p>
<p className="text-sm text-gray-600">Client: {d.client} vs Freelancer: {d.freelancer}</p>
</div>
<div className="space-x-2">
<button className="px-3 py-1 bg-green-100 text-green-700 rounded hover:bg-green-200">Rule Freelancer</button>
<button className="px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200">Rule Client</button>
</div>
</div>
))}
</section>

<section className="bg-white p-6 rounded shadow">
<h2 className="text-xl font-bold mb-4">Flagged Jobs</h2>
{flaggedJobs.map(j => (
<div key={j.id} className="border p-4 rounded mb-2 flex justify-between items-center">
<div>
<p className="font-semibold">{j.title}</p>
<p className="text-sm text-red-600">Reason: {j.flaggedReason}</p>
</div>
<div className="space-x-2">
<button className="px-3 py-1 bg-green-600 text-white rounded">Approve</button>
<button className="px-3 py-1 bg-red-600 text-white rounded">Reject & Notify</button>
</div>
</div>
))}
</section>
</div>
)}

{/* CONTROLS */}
{activeTab === "controls" && (
<section className="bg-white p-6 rounded shadow space-y-6">
<h2 className="text-xl font-bold mb-4">Platform Controls</h2>

<div className="flex justify-between items-center border-b pb-4">
<div>
<h3 className="font-bold">New User Registrations</h3>
<p className="text-sm text-gray-500">Allow new users to sign up on the platform.</p>
</div>
<button
onClick={() => handleToggle("registrations", registrationsEnabled)}
className={`px-6 py-2 rounded font-bold text-white transition-colors ${registrationsEnabled ? 'bg-green-500 hover:bg-green-600' : 'bg-red-500 hover:bg-red-600'}`}
>
{registrationsEnabled ? "ENABLED" : "DISABLED"}
</button>
</div>

<div className="flex justify-between items-center border-b pb-4">
<div>
<h3 className="font-bold">New Job Postings</h3>
<p className="text-sm text-gray-500">Allow clients to post new jobs to the marketplace.</p>
</div>
<button
onClick={() => handleToggle("postings", postingsEnabled)}
className={`px-6 py-2 rounded font-bold text-white transition-colors ${postingsEnabled ? 'bg-green-500 hover:bg-green-600' : 'bg-red-500 hover:bg-red-600'}`}
>
{postingsEnabled ? "ENABLED" : "DISABLED"}
</button>
</div>

<div className="pt-4">
<h3 className="font-bold mb-2">Recent Audit Logs</h3>
<ul className="text-sm font-mono text-gray-600 bg-gray-100 p-4 rounded max-h-40 overflow-y-auto">
<li>[2026-05-18 10:45] Admin ID (auth_21) suspended User Bob</li>
<li>[2026-05-18 10:42] Admin ID (auth_21) viewed dispute #d1</li>
</ul>
</div>
</section>
)}
</div>
);
}
}
71 changes: 71 additions & 0 deletions benchmarks/benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import http from 'k6/http';
import { check, sleep } from 'k6';
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";

// Load test thresholds and endpoints
export const options = {
vus: 50,
duration: '30s',
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 99% of requests must complete below 1s
http_req_failed: ['rate<0.01'], // less than 1% errors
},
};

const BASE_URL = __ENV.TARGET_URL || 'http://localhost:3000';
const TOKEN = __ENV.BENCHMARK_TOKEN || 'test-benchmark-token';

const endpoints = [
{ method: 'GET', url: '/api/auth/health', auth: false },
{ method: 'POST', url: '/api/auth/login', auth: false, body: { email: 'test@https://crypto-alpha-hub-clean-2026.surge.sh', password: 'password' } },
{ method: 'GET', url: '/api/jobs', auth: false },
{ method: 'GET', url: '/api/admin/metrics', auth: true },
{ method: 'POST', url: '/api/messages', auth: true, body: { text: "Hello", to: "user_123" } },
];

export default function () {
const params = {
headers: {
'Content-Type': 'application/json',
},
};

// Pick a random endpoint
const idx = Math.floor(Math.random() * endpoints.length);
const ep = endpoints[idx];

if (ep.auth) {
params.headers['Authorization'] = `Bearer ${TOKEN}`;
}

let res;
if (ep.method === 'GET') {
res = http.get(`${BASE_URL}${ep.url}`, params);
} else {
res = http.post(`${BASE_URL}${ep.url}`, JSON.stringify(ep.body), params);
}

check(res, {
'status is 200 or 201': (r) => r.status === 200 || r.status === 201,
'TTFB < 300ms': (r) => r.timings.waiting < 300,
});

sleep(0.1);
}

export function handleSummary(data) {
// Return markdown summary
const md = `
# Benchmark Results
- **Total Requests**: ${data.metrics.http_reqs.values.count}
- **p50 Latency**: ${data.metrics.http_req_duration.values.p(50).toFixed(2)} ms
- **p95 Latency**: ${data.metrics.http_req_duration.values.p(95).toFixed(2)} ms
- **p99 Latency**: ${data.metrics.http_req_duration.values.p(99).toFixed(2)} ms
- **RPS**: ${data.metrics.http_reqs.values.rate.toFixed(2)}
- **Error Rate**: ${(data.metrics.http_req_failed.values.rate * 100).toFixed(2)}%
`;
return {
"summary.md": md,
"results.json": JSON.stringify(data),
};
}
4 changes: 4 additions & 0 deletions benchmarks/thresholds.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"p95": 500,
"p99": 1000
}