Initial commit
This commit is contained in:
23
app/api/rss/route.ts
Normal file
23
app/api/rss/route.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import Parser from 'rss-parser';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET() {
|
||||
const parser = new Parser();
|
||||
try {
|
||||
const feed = await parser.parseURL('https://news.ycombinator.com/rss');
|
||||
|
||||
const items = feed.items.slice(0, 10).map(item => ({
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
pubDate: item.pubDate,
|
||||
creator: item.creator || 'Unknown',
|
||||
contentSnippet: item.contentSnippet,
|
||||
}));
|
||||
|
||||
return NextResponse.json(items);
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: 'Failed to parse RSS' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
53
app/api/status/route.ts
Normal file
53
app/api/status/route.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Configuration - Update these URLs to match your actual services
|
||||
const SERVICES = [
|
||||
{ name: 'Website', url: 'https://www.akkolli.net' },
|
||||
{ name: 'Gitea', url: 'https://code.akkolli.net' },
|
||||
{ name: 'Nextcloud', url: 'http://localhost:6060' },
|
||||
];
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET() {
|
||||
const results = await Promise.all(
|
||||
SERVICES.map(async (service) => {
|
||||
const start = performance.now();
|
||||
try {
|
||||
// Set a short timeout (e.g., 5 seconds)
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
const response = await fetch(service.url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal,
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
const end = performance.now();
|
||||
const latency = Math.round(end - start);
|
||||
|
||||
return {
|
||||
name: service.name,
|
||||
url: service.url,
|
||||
status: response.ok ? 'up' : 'down',
|
||||
latency: latency,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
name: service.name,
|
||||
url: service.url,
|
||||
status: 'down',
|
||||
latency: 0,
|
||||
timestamp: new Date().toISOString(),
|
||||
error: 'Unreachable'
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return NextResponse.json(results);
|
||||
}
|
||||
BIN
app/favicon.ico
Normal file
BIN
app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
26
app/globals.css
Normal file
26
app/globals.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
22
app/layout.tsx
Normal file
22
app/layout.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
26
app/page.tsx
Normal file
26
app/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { GridShell } from "@/components/dashboard/GridShell";
|
||||
import { UptimeCard } from "@/components/widgets/UptimeCard";
|
||||
import { GlobeCard } from "@/components/widgets/GlobeCard";
|
||||
import { WeatherCard } from "@/components/widgets/WeatherCard";
|
||||
import { NewsFeed } from "@/components/widgets/NewsFeed";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<GridShell>
|
||||
{/* Row 1 */}
|
||||
<UptimeCard />
|
||||
<WeatherCard />
|
||||
|
||||
{/* Row 1 & 2 (Globe spans 2 rows) */}
|
||||
<GlobeCard />
|
||||
|
||||
{/* Row 2 */}
|
||||
<NewsFeed />
|
||||
|
||||
{/* Future widget placeholder to fill grid if needed */}
|
||||
<div className="col-span-1 md:col-span-2 lg:col-span-2 row-span-1 bg-neutral-900/50 border border-neutral-800/50 rounded-xl flex items-center justify-center border-dashed">
|
||||
<span className="text-xs text-neutral-600 font-mono">System Metric Placeholder</span>
|
||||
</div>
|
||||
</GridShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user