Skip to content
Merged
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
99 changes: 36 additions & 63 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,70 +87,20 @@ const dashboardStats = [
},
]

const recentActivities = [
{
id: 1,
type: "user_signup",
message: "New user registered: akshay@gmail.com",
timestamp: "2 minutes ago",
status: "success",
},
{
id: 2,
type: "test_created",
message: "New test 'JavaScript Fundamentals' created",
timestamp: "5 minutes ago",
status: "success",
},
{
id: 3,
type: "blog_published",
message: "Blog post 'React 18 Features' published by Akshay",
timestamp: "15 minutes ago",
status: "success",
},
{
id: 4,
type: "event_created",
message: "New hackathon 'Summer Challenge 2025' created",
timestamp: "1 hour ago",
status: "info",
},
{
id: 5,
type: "security_alert",
message: "Multiple failed login attempts detected",
timestamp: "2 hours ago",
status: "warning",
}
]
type Activity = {
id: number
type: string
message: string
timestamp: string
status: string
}

const systemHealth = [
{
service: "API Server",
status: "healthy",
uptime: "99.9%",
responseTime: "45ms",
},
{
service: "Database",
status: "healthy",
uptime: "99.8%",
responseTime: "12ms",
},
{
service: "CDN",
status: "healthy",
uptime: "100%",
responseTime: "23ms",
},
{
service: "Email Service",
status: "warning",
uptime: "98.5%",
responseTime: "156ms",
},
]
type SystemHealthItem = {
service: string
status: string
uptime: string
responseTime: string
}

export default function AdminDashboard() {
const [currentTime, setCurrentTime] = useState(new Date())
Expand All @@ -162,6 +112,8 @@ export default function AdminDashboard() {
const [pageViewsChange, setPageViewsChange] = useState<string>("")
const [pageViewsTrend, setPageViewsTrend] = useState<"up" | "down">("up")
const [topContent, setTopContent] = useState<BlogPost[]>([])
const [recentActivities, setRecentActivities] = useState<Activity[]>([])
const [systemHealth, setSystemHealth] = useState<SystemHealthItem[]>([])
const supabaseRef = useRef<ReturnType<typeof createClient> | null>(null)
const likesChannelRef = useRef<RealtimeChannel | null>(null)

Expand Down Expand Up @@ -249,6 +201,27 @@ export default function AdminDashboard() {
}
}
fetchTopContentWithLikes()

// Fetch recent activities
fetch("/api/admin/recent-activities")
.then(res => res.json())
.then(data => {
if (data.activities) {
setRecentActivities(data.activities)
}
})
.catch(err => console.error("Failed to fetch activities:", err))

// Fetch system health
fetch("/api/admin/system-health")
.then(res => res.json())
.then(data => {
if (data.health) {
setSystemHealth(data.health)
}
})
.catch(err => console.error("Failed to fetch system health:", err))

// Setup realtime subscription
const supabase = createClient()
supabaseRef.current = supabase
Expand Down
139 changes: 139 additions & 0 deletions app/api/admin/recent-activities/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { NextResponse } from "next/server"
import { createClient } from "@/lib/supabase/server"

export async function GET() {
try {
const supabase = await createClient()

// Check if user is admin
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}

const { data: profile } = await supabase
.from("profiles")
.select("is_admin")
.eq("id", user.id)
.single()

if (!profile?.is_admin) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
}

const activities: Array<{
type: string
message: string
timestamp: string
status: string
created_at: string
}> = []

// Fetch recent user signups (last 7 days to ensure we have data)
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()

const { data: recentUsers } = await supabase
.from("profiles")
.select("email, created_at")
.gte("created_at", sevenDaysAgo)
.order("created_at", { ascending: false })
.limit(3)

if (recentUsers) {
recentUsers.forEach(user => {
activities.push({
type: "user_signup",
message: `New user registered: ${user.email}`,
timestamp: getTimeAgo(user.created_at),
status: "success",
created_at: user.created_at
})
})
}

// Fetch recent blog posts
const { data: recentBlogs } = await supabase
.from("blogs")
.select("title, author, date")
.order("date", { ascending: false })
.limit(3)

if (recentBlogs) {
recentBlogs.forEach(blog => {
activities.push({
type: "blog_published",
message: `Blog post "${blog.title}" published by ${blog.author}`,
timestamp: getTimeAgo(blog.date),
status: "success",
created_at: blog.date
})
})
}

// Fetch recent events
const { data: recentEvents } = await supabase
.from("events")
.select("title, created_at")
.order("created_at", { ascending: false })
.limit(2)

if (recentEvents) {
recentEvents.forEach(event => {
activities.push({
type: "event_created",
message: `New event "${event.title}" created`,
timestamp: getTimeAgo(event.created_at),
status: "info",
created_at: event.created_at
})
})
}

// Fetch recent hackathons
const { data: recentHackathons } = await supabase
.from("hackathons")
.select("title, created_at")
.order("created_at", { ascending: false })
.limit(2)

if (recentHackathons) {
recentHackathons.forEach(hackathon => {
activities.push({
type: "event_created",
message: `New hackathon "${hackathon.title}" created`,
timestamp: getTimeAgo(hackathon.created_at),
status: "info",
created_at: hackathon.created_at
})
})
}

// Sort all activities by timestamp and limit to 5
activities.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
const topActivities = activities.slice(0, 5).map((activity, index) => ({
id: index + 1,
type: activity.type,
message: activity.message,
timestamp: activity.timestamp,
status: activity.status
}))

return NextResponse.json({ activities: topActivities })
} catch (error) {
console.error("Recent activities error:", error)
return NextResponse.json({ error: "Internal server error" }, { status: 500 })
}
}

function getTimeAgo(dateString: string): string {
const date = new Date(dateString)
const now = new Date()
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000)

if (seconds < 60) return `${seconds} seconds ago`
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`
if (seconds < 604800) return `${Math.floor(seconds / 86400)} days ago`
return `${Math.floor(seconds / 604800)} weeks ago`
}
79 changes: 79 additions & 0 deletions app/api/admin/system-health/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { NextResponse } from "next/server"
import { createClient } from "@/lib/supabase/server"

export async function GET() {
try {
const supabase = await createClient()

// Check if user is admin
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}

const { data: profile } = await supabase
.from("profiles")
.select("is_admin")
.eq("id", user.id)
.single()

if (!profile?.is_admin) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
}

const healthChecks = []

// Check Database
const dbStart = Date.now()
const { error: dbError } = await supabase
.from("profiles")
.select("id")
.limit(1)
const dbTime = Date.now() - dbStart

healthChecks.push({
service: "Database",
status: dbError ? "error" : dbTime < 100 ? "healthy" : "warning",
uptime: dbError ? "Down" : "Operational",
responseTime: `${dbTime}ms`
})

// Check API Server (this endpoint itself)
const apiTime = Date.now() - dbStart
healthChecks.push({
service: "API Server",
status: "healthy",
uptime: "Operational",
responseTime: `${apiTime}ms`
})

// Check Supabase Storage
const storageStart = Date.now()
const { error: storageError } = await supabase.storage
.from("blog-images")
.list("public", { limit: 1 })
const storageTime = Date.now() - storageStart

healthChecks.push({
service: "Storage (CDN)",
status: storageError ? "error" : storageTime < 300 ? "healthy" : "warning",
uptime: storageError ? "Down" : "Operational",
responseTime: `${storageTime}ms`
})

// Check Email Service (Resend)
const resendConfigured = !!process.env.RESEND_API_KEY
healthChecks.push({
service: "Email Service",
status: resendConfigured ? "healthy" : "warning",
uptime: resendConfigured ? "Configured" : "Not Configured",
responseTime: resendConfigured ? "Ready" : "N/A"
})

return NextResponse.json({ health: healthChecks })
} catch (error) {
console.error("System health error:", error)
return NextResponse.json({ error: "Internal server error" }, { status: 500 })
}
}
Loading